I found myself in a situation where I wanted to playback recently downloaded videos from YouTube play list. Because the file names had space characters in them, a normal invocation of xargs wouldn't have helped. Instead I'd have to somehow separate the file names with null characters, and use xargs -0 to ask it to split input at null characters instead of whitespace.
The plan was to get the list of latest downloaded files and substitute new-line characters with null character to form a single string. As usual, I turned to sed and composed this stress-free one-liner:
ls -r | tail -n 10 | sed '${s/\n/\x00/g};N' | xargs -0 mplayer
The intention was to keep appending the input lines to the pattern space, and substitute all new-line characters with null character and print the output. Of course I expected this to work, but instead, I could see mplayer playing only the latest file downloaded! This meant that sed was emitting only the last of the input lines, which was a bit surprising.
After many tries, I realized that I took sed's processing mechanism for granted yet again. The way sed works is that it starts with empty pattern space and execute the entire program on it. When the program finishes, it reads the next line from input and restarts the cycle. This meant that the pattern space is cleared before the next input line is processed. I.e. the N command at the end of the program did append the current line to the pattern space, but it was cleared immediately as the cycle restarted! As a result, by the time it got to the substitution, only the last line remained in the pattern space.
What I had to do instead was to prevent the cycle from restarting by adding a loop:
ls -r | tail -n 10 | sed ':a;${s/\n/\x00/g};N;b a' | xargs -0 mplayer
Here, I define a label a at the beginning of the program. After appending current line to the pattern space, I jump to the label instead of letting the cycle restart. This ensures that the pattern space has all the lines appended by the time we get to the substitution. The substitution then replaces all new-line characters with null characters; and xargs gives mplayer all the files I intended.
Another lesson learned was to use tr instead, which requires far less key strokes:
lr | tail -n 10 | tr '\n' '\0' | xargs -0 mplayerFor me, tr is proving more and more useful for carrying trivial substitution like this. Maybe I should give it the attention it deserves.
No comments:
Post a Comment