2

I want tail -f /var/log/syslog | grep it with pattern "arpwatch" and send every line to myself via jabber: xmpp username@jabber.server using xargs

tail -f /var/log/syslog | grep arpwatch | xargs sendxmpp username@jabber.server

no working.

but tail /var/log/syslog | grep arpwatch | sendxmpp username@jabber.server

works well.

I think it's some fundamental thing about xargs and tail -f that I don't understand.

3

xargs command tries to collect as many input items (lines, words) as it can to one invocation of the command, and it doesn't particularly care about the timing of the input data. If the tail process is killed, or xargss buffer get filled, it will run command with the arguments it received by then. However, tail -f usually doesn't finish by itself, and the limit for command line arguments can be large, so it seems like it doesn't work at all.

You could use xargs -n1 to have it pass only one input item at a time to command, but you'll be hit by the fact that xargs uses whitespace to split the input into items, so an input line of foo bar would cause the command to run twice.

With GNU xargs, xargs -n1 -d '\n' should do what you want: run command once for each input line, with the full line passed as a single argument.

Try, e.g. with and without the -d and -n and make note of the output timing:

$ ( echo "123 456"; sleep 1; echo foo; sleep 1; echo doo ) | xargs -d '\n' -n1 printf ':%s\n'

xargs -L 1 would also work, but it will still split the line to separate arguments, instead of passing the whole line as one argument.

2

grep has the option --line-buffered to output each line immediately instead of waiting for more input.

For xargs, as suggested by @ikkachu, you need to separate by newlines instead of spaces. You can use xargs -L for that.

This should work:

tail -f /var/log/syslog \
  | grep --line-buffered arpwatch \
  | xargs -L1 sendxmpp username@jabber.server
  • -L1 is not to separate by newline, but to run one command per line with as many words as there are on the line as separate arguments; IOW, space, tab (and other whitespace depending on the implementation) and quotes/blackslash are still used as delimiters. To split by newline, you need either GNU's -d '\n' or escape all other blanks and quoting operators supported by xargs. Or with some implementations that suport -0 and not -d, use tr '\n' '\0' in combination with xargs -0. – Stéphane Chazelas Dec 19 '18 at 14:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.