Search code examples
stringbashsedexpr

expr bash for sed a line in log does not work


my goal is to sed the 100th line and convert it to a string, then separate the data of the sentence to word

#!/bin/bash
fid=log.txt;

sentence=`expr sed -n '100p' ${fid}`;
for word in $sentence
do
    echo $word
done

but apparently this has failed.

expr: syntax error

would somebody please let me know what have I done wrong? previously for number it worked.


Solution

  • The expr does not seem to serve a useful purpose here, and if it did, a sed command would certainly not be a valid or useful thing to pass to it, under most circumstances. You should probably just take it out.

    However, the following loop is also problematic. Unquoted variables in shell script are very frequently an error. In this case, you can't quote the thing you pass to the for loop (that would cause the loop to only run once, with the loop variable set to the quoted string) but you also cannot prevent the shell from performing wildcard expansion on the unquoted string. So if the string happened to contain *, the shell will expand that to a list of files in the current directory, for example.

    Fortunately, this can all be done in an only slightly more complicated sed script.

    sed '100!d;s/[ \t]\+/\n/g;q' "$fid"
    

    That is, if the line number is not 100, delete this line and start over with the next line. Otherwise, we are at line 100; replace runs of whitespace with newlines, (print) and quit.

    (The backslash escape codes \t and \n are not universally portable; and \+ for repetition is also an optional extension. I believe there are also sed variants which dislike semicolon as a command separator. Consult your sed manual page, experiment, and if everything else fails, maybe switch to Awk or Perl. Just in case, here is a version which works even on Mac OSX:

    sed '100!d
         s/[    ][  ]*/\
    /g;q' log.txt
    

    The stuff inside the square brackets are a space and a literal tab; in Bash, with default keybindings, type ctrl-V, tab to produce a literal tab.)

    Incidentally, this also gets rid of the variable capture antipattern. There are good reasons to capture output to a variable, but if it can be avoided, you often end up with a simpler, more robust and efficient, as well as more idiomatic and elegant script. (I see no reason to put the log file name in a variable, either, in this isolated case; but in a larger script, it might make sense.)