Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a pipe to read a file, run script and write to the same file

Tags:

bash

I need to write a script with one line that gets a file and print on the same file on the end of each line the numbers of words on the sentence only if the word "word" Appears on it. I can use another script that can do what ever I want. My problem is that after I run the script the file is empty, the file that I sent to the script.

This is the one line script:

#!/bin/bash
cat $1 | ./words_num word | cat $1

words_num

#!/bin/bash
while read line; do
    temp=`echo $line | grep $1 | wc -l`
    if (($temp==1)); then
        word_cnt=`echo $line | wc -w`
        echo "$line $word_cnt" 
    else 
        echo "$line"
    fi  
done

For example, before the file is:

bla bla blaa word
words blaa
bla bla

after file:

bla bla blaa word 4
words blaa 2
bla bla

Can you help?

like image 663
user2304488 Avatar asked Apr 21 '13 14:04

user2304488


2 Answers

The one-liner:

cat $1 | ./words_num word | cat $1

is peculiar. It is approximately equivalent to:

cat $1 | ./words_num word >/dev/null; cat $1

which is unlikely to be the intended result. It is also a candidate for a UUOC (Useless Use of cat) award.

If the intention is to overwrite the original file with the amended version, then you should probably write:

./words_num word < $1 > tmp.$$; mv tmp.$$ $1

If you want to see the results on the screen as well, then:

./words_num word < $1 | tee tmp.$$; mv tmp.$$ $1

Both these will leave a temporary file around if interrupted. You can avoid that with:

#!/bin/bash
trap "rm -f tmp.$$; exit 1" 0 1 2 3 13 15
./words_num word < $1 | tee tmp.$$
mv tmp.$$ $1
trap 0

The trap sets signal handlers (EXIT, HUP, INT, QUIT, PIPE, TERM) and removes the temporary file (if it exists) and exits with a failure status. The trap 0 at the end cancels the exit trap so the command exits successfully.

As for the words_num script, that seems to call for awk rather than shell:

#!/bin/bash
[ $# == 0 ] && { echo "Usage: $0 word [file ...]" >&2; exit 1; }
word=$1
shift
awk "/$word/"' { print $0, NF; next } { print }' "$@"

You can reduce that if you're into code golfing your awk scripts, but I prefer clarify to sub-par code. It looks for lines containing the word, prints the line along with the number of fields in the line, and moves to the next line. If the line doesn't match, it is simply printed. The assignment and shift mean that "$@" contains all the other arguments to words_num, and awk will automatically cycle through the named files, or read standard input if no files are named.

The script should check that the given word does not contain any slashes as that will mess up the regex (it would be OK to replace each one that appears with [/], a character class containing only a slash). That level of bullet-proofing is left for the interested user.

like image 65
Jonathan Leffler Avatar answered Nov 15 '22 07:11

Jonathan Leffler


cat $1 | ./words_num word | tee $1
like image 30
ennuikiller Avatar answered Nov 15 '22 09:11

ennuikiller