Fdonadon
Fdonadon

Reputation: 87

Bash while read line loop does not print every line in condition

I have the following situation:

I have a text file I'm trying to loop so I can know if each line has a match with ".mp3" in this case which is this one:

12 Stones.mp3
randomfile.txt
Aclarion.mp3
ransomwebpage.html
Agents Of The Sun.mp3
randomvideo.mp4

So, I've written the following script to process it:

while read line || [ -n "$line" ]
do
varline=$(awk '/.mp3/{print "yes";next}{print "no"}')
echo $varline
if [ "$varline" == "yes" ]; then
        some-command
    else
         some-command
    fi
done < file.txt

The expected output would be:

yes
no
yes
no
yes
no

Instead, it seems misses the first line and I get the following:

no
yes
no
yes
no

Upvotes: 2

Views: 1527

Answers (3)

Tyl
Tyl

Reputation: 5252

Have you forgot something? Your awk has no explicit input, change to this instead:

while IFS= read -r read line || [ -n "$line" ]
do
varline=$(echo "$line" | awk '/.mp3/{print "yes";next}{print "no"}')
echo $varline
if [ "$varline" == "yes" ]; then
        some-command
    else
        some-other-command
    fi
done < file.txt

In this case, you might need to change to /\.mp3$/ or /\.mp3[[:space:]]*$/ for precise matching.
Because . will match any character, so for example /.mp3/ will match Exmp3but.mp4 too.
Update: changed while read line to while IFS= read -r read line, to keep each line's content intact when assigning to the variable.

And the awk part can be improved to:

awk '{print $0~/\.mp3$/ ? "yes":"no"}'

So with awk only, you can do it like this:

awk '{print $0~/\.mp3$/ ? "yes":"no"}' file.txt

Or if your purpose is just the commands in the if structure, you can just do this:

awk '/\.mp3$/{system("some-command");next}{system("some-other-command");}' file.txt

or this:

awk '{system($0~/\.mp3$/ ? "some-command" : "some-other-command")}' file.txt

Upvotes: 2

tripleee
tripleee

Reputation: 189689

You really don't need Awk for a simple pattern match if that's all you used it for.

while IFS= read -r line; do
    case $line in
     *.mp3) some-command;,
     *) some-other-command;;
    esac
done <file.txt

If you are using Awk anyway for other reasons, looping the lines in a shell loop is inefficient and very often an antipattern. This doesn't really fix that, but at least avoids executing a new Awk instance on every iteration:

awk '{ print ($0 ~ /\.mp3$/) ? "yes" : no" }' file.txt |
while IFS= read -r whether; do
    case $whether in
     'yes') some-command ;;
     'no') some-other-command;;
    esac
done

If you need the contents of "$line" too, printing that from Awk as well and reading two distinct variables is a trivial change.

I simplified the read expression on the assumption that you can make sure your input file is well-formed separately. If you can't do that, you need to put back the more-complex guard against a missing newline on the last line in the file.

Upvotes: 4

Sonny
Sonny

Reputation: 3183

Use awk

$ awk '{if ($0 ~ /mp3/) {print "yes"} else {print "no"}}' file.txt
yes
no
yes
no
yes
no

Or more concise:

$ awk '/mp3/{print "yes";next}{print "no"}' file.txt
$ awk '{print (/mp3/ ? "yes" : "no")}' file.txt

Upvotes: 2

Related Questions