Executing commands containing a space in bash

I have a file called cmd that contains a list of unix commands as follows:

hostname pwd ls /tmp cat /etc/hostname ls -la ps -ef | grep java cat cmd 

I have another script that executes commands in cmd like:

 IFS=$'\n' clear for cmds in `cat cmd` do if [ $cmds ] ; then $cmds; echo "****************************"; fi done 

The problem is that commands in cmd without spaces work fine, but those with spaces do not correctly interpret the script. The following is the conclusion:

 patrick-laptop **************************** /home/patrick/bashFiles **************************** ./prog.sh: line 6: ls /tmp: No such file or directory **************************** ./prog.sh: line 6: cat /etc/hostname: No such file or directory **************************** ./prog.sh: line 6: ls -la: command not found **************************** ./prog.sh: line 6: ps -ef | grep java: command not found **************************** ./prog.sh: line 6: cat cmd: command not found **************************** 

What am I missing here?

+7
bash shell
source share
6 answers

Try changing one line to eval $cmds , not just $cmds

+13
source share

You can replace the script with the command

 sh cmd 

The task of the shell is to read commands and run them! If you need output / progress indicators, run the shell in verbose mode

 sh -v cmd 
+3
source share

I personally like this approach better - I do not want to use IFS if I do not need it. You need to use eval if you are going to use pipes in your commands. The pipe should be processed by the sheath, not a team. I believe the shell parses the pipes before expanding strings.

Note that if your cmd file contains commands that enter input, there will be a problem. (But you can always create a new fd for a read-to-read command.)

 clear while read cmds do if [ -n "$cmds" ] ; then eval $cmds echo "****************************"; fi done < cmd 
+2
source share

Edit: Turns off this pipe failure and redirection. Thanks, Andomar.

You need to change the IFS back inside the loop so that bash knows where to split the arguments:

 IFS=$'\n' clear for cmds in `cat cmd` do if [ $cmds ] ; then IFS=$' \t\n' # the default $cmds; echo "****************************"; IFS=$'\n' fi done 
+1
source share

EDIT: Ben Blanca's comment noted that my old answer was wrong, thanks.

It looks like you are executing the commands as a single line, so bash sees them as a script / executable name.

One way to avoid this is to invoke the bash command on the command. Change:

  if [ $cmds ] ; then $cmds; echo "****************************"; fi 

to

  if [ $cmds ] ; then bash -c $cmds echo "****************************"; fi 
0
source share
 sed 'aecho "-----------"' cmd > cmd.sh; bash cmd.sh 

sed 'aXX' adds XX to each line. This will not work for multi-line commands, for example:

 for f in * do something $f fi 

but for single-line commands in most cases this needs to be done.

0
source share

All Articles