views:

813

answers:

3

I have a file containing a list of unix commands like "ls -la /etc/passwd". I want to iterate through this list of commands, and take appropriate action.

Currently, my code is something like:

#!/bin/bash
clear
for cmds in `cat /tmp/cmd`
do
        if [  $cmds ] ; then
                echo $cmds;
        fi
done

But, what I get is an output where the commands are broken up in different lines like

ls 
-la 
/etc/passwd
ls 
-la
/etc/shadow

and not

ls -la /etc/passwd
ls -la /etc/shadow

Any ideas?

+1  A: 

Add this before your for loop:

IFS='
'

Or in Bash:

IFS=$'\n'

IFS contains a list of characters which are used to split input into fields; it defaults to including spaces, tabs, and linefeeds, which was why you were getting the incorrect behavior. Setting it to a newline will only split on newlines, which is what you want.

Brian Campbell
Your solution partly worked, but all the n's were also used to split the input fields. And, it gave incorrect output like /etc/ssh/sshd_cofigsvcs -a | grep etworketstat -a | grep -i listeAs you can see all n's were lost and incoorectly denoted new lines.
Epitaph
Sorry. It appears that my version of Bash interprets \n in IFS specially; in generally, though, that won't work for putting a newline into a string. I've edited my answer to be more portable; you can put a literal newline in the string, or use Bash's C-style quotation.
Brian Campbell
+1  A: 

I believe you can also do this (surround your backticks with double-quotes):

"`cat /tmp/cmds`"

Maybe try something like this (not sure what your "if" is supposed to be checking - I modified it to make sure the $cmds var was not empty string):

#!/bin/bash

clear

for cmds in "`cat cmds.txt`"
do
    if [ "$cmds" != '' ]; then
        echo "$cmds"
    fi
done
Andy White
I got an error after putting in the double quotes/tmp/fo: line 6: [: too many arguments
Epitaph
Added an example, not sure if this will work for you :)
Andy White
+1  A: 

If each line in the file is a complete command - no continuations over multiple lines - then you can use read:

while read cmd
do
    if [ -z "$cmd" ]
    then : Empty
    elif $cmd
    then : OK
    else : Oops
    fi
done < cmds.txt

Or, if you prefer linear construction and don't need anything from the sub-shell that the while loop represents in what follows, you can use:

cat cmds.txt |
while read cmd
do
    if [ -z "$cmd" ]
    then : Empty
    elif $cmd
    then : OK
    else : Oops
    fi
done

Note the careful use of quotes around the tested string. I still prefer to use an explicit '-z' operator; there are those who argue it is not 100% necessary. I still use single square brackets rather than double - mainly out of habit built up over 20+ years. Those who learned shell using bash or ksh often prefer the double square bracket operator instead.

Beware I/O redirections, of course.

Also, if you don't have anything to do in the various then and else clauses, you should simply feed cmds.txt to the shell:

sh cmds.txt

This is by far the most reliable way of dealing with multi-line commands.

Jonathan Leffler