views:

2093

answers:

7

Hello!

I bumped into the following problem: I'm writing a Linux batch script which does the following:

  • Read line from file
  • Strip the \n character from the end of the line just read
  • Execute the command that's in there

Example: commands.txt

ls
ls -l
ls -ltra
ps as

The execution of the batch file should get the first line, and execute it, but while the \n present, the shell just outputs "command not found: ls" That part of the script looks like this

 read line

        if [ -n "$line" ]; then #if not empty line

                #myline=`echo -n $line | tr -d '\n'`
                #myline=`echo -e $line | sed ':start /^.*$/N;s/\n//g; t start'`

                myline=`echo -n $line | tr -d "\n"`
                $myline  #execute it

                cat $fname | tail -n+2 > $fname.txt
                mv $fname.txt $fname
        fi

Commented you have the things I tried before asking SO. Any solutions? I'm smashing my brains for the last couple of hours over this...

A: 

though not working for ls, I recommend having a look at find’s -print0 option

knittl
A: 

I tried this:

read line
echo -n $line | od -x

For the input 'xxxx', I get:

0000000 7878 7878

As you can see, there is no \n at the end of the contents of the variable. I suggest to run the script with the option -x (bash -x script). This will print all commands as they are executed.

[EDIT] Your problem is that you edited commands.txt on Windows. Now, the file contains CRLF (0d0a) as line delimiters which confuses read (and ls\r is not a known command). Use dos2unix or similar to turn it into a Unix file.

Aaron Digulla
The thing is i don't read from keyboard. I change my STDIN to a file (the command.txt from above) and if I echo the $line in between '' I get the output 'ls'
Bogdan Constantinescu
The ouput I get on `echo -n $line | od -x` is "0000000 736c 000d0000003"
Bogdan Constantinescu
The 0d is a carriage return. Not the same thing as a newline
cms
AH! You edited that file on Windows and now, you have CRs in it! Use dos2unix or a similar script to convert CRLF (0d0a) to LF (0a).
Aaron Digulla
+5  A: 

I always like perl -ne 'chomp and print' , for trimming newlines. Nice and easy to remember.

e.g. ls -l | perl -ne 'chomp and print'

However

I don't think that is your problem here though. Although I'm not sure I understand how you're passing the commands in the file through to the 'read' in your shell script.

With a test script of my own like this (test.sh)

read line
if [ -n "$line" ]; then
  $line
fi

and a sample input file like this (test.cmds)

ls 
ls -l
ls -ltra

If I run it like this ./test.sh < test.cmds, I see the expected result, which is to run the first command 'ls' on the current working directory.

Perhaps your input file has additional non-printable characters in it ?

mine looks like this

od -c test.cmds 
0000000    l   s      \n   l   s       -   l  \n   l   s       -   l   t
0000020    r   a  \n                                                    
0000023

From your comments below, I suspect you may have carriage returns ( "\r" ) in your input file, which is not the same thing as a newline. Is the input file originally in DOS format ? If so, then you need to convert the 2 byte DOS line ending "\r\n" to the single byte UNIX one, "\n" to achieve the expected results.

You should be able to do this by swapping the "\n" for "\r" in any of your commented out lines.

cms
I forgot to mention that the file is written from Windows in a samba share and I totally forgot that I should look for a `\r` instead of `\n`. Up one vote from me and many thanks!
Bogdan Constantinescu
+1  A: 

you need eval command


#!/bin/bash -x

while read  cmd
do
 if [ "$cmd" ]
 then
  eval "$cmd"
 fi
done
I ran it as ./script.sh < file.txt And file.txt was:
ls
ls -l
ls -ltra
ps as
Neeraj
`eval` won't work also
Bogdan Constantinescu
A: 

The following script works (at least for me):

#!/bin/bash

while read I ; do if [ "$I" ] ; then $I ; fi ; done ;
dsm
+3  A: 
Mark Edgar
Yes, of course, but that part of my batch file. It's more than a simple execute command from file, cause if it was just this I've used for sure only `sh`
Bogdan Constantinescu
A: 

You may also try to replace carriage returns with newlines only using Bash builtins:

line=$'a line\r' 
line="${line//$'\r'/$'\n'}" 
#line="${line/%$'\r'/$'\n'}"       # replace only at line end
printf "%s" "$line" | ruby -0777 -n -e 'p $_.to_s'