0

I have a strange problem with xargs.
I have an xargs construct which does not work, even though when I echo the command, it works perfectly. My one liner is the following:

 exiftool -p exifprintformat  -if '$Subject =~/DATA/i' -q  *.pdf |grep pdf |sed 's/ //g'|xargs|xargs -0 -I % pdftk % cat output binder1.pdf  

and the output

Error: Unable to find file. Error: Failed to open PDF file: 20170105170516234.pdf 20170105173126944.pdf 20170105173209758.pdf 20170621163418079.pdf

  • The exiftool selects all the pdf that contain the word DATA in the subject tag,
  • The -p exifprintformat instructs exiftool to print only the file name,
  • The grep selects only the lines with pdf,
  • The sed removes whitespaces,
  • The first xarg makes all the lines into one string and the second constructs the bind command) when I run

exiftool -p exifprintformat -if '$Subject =~/DATA/i' -q *.pdf |grep pdf |sed 's/ //g'|xargs|xargs -I{} echo pdftk {} cat output binder1.pdf

I get

pdftk 20170105170516234.pdf 20170105173126944.pdf 20170105173209758.pdf 20170621163418079.pdf cat output binder1.pdf

which works perfectly.

Obviously, I am doing something wrong...But what?

  • grep expects line endings (probably present) and darfst -0 expects NUL bytes (probably missing) – eckes Jun 21 '17 at 18:50
0

There are several things wrong, and unnecessary complexity:-

  1. The double xargs call means that the second sees a single line of input, so {} is substituted just once with a single string comprising all the matching file names, but echo does not show this difference in the output (compare echo a b with echo "a b").
  2. The -0 argument means that xargs needs a null ('\0') character between input arguments, and there are none; this also forces the input to be treated as a single parameter.
  3. By outputting only the file name when the condition matches, you get one file name per line, which can be piped directly to xargs without the need for grep or sed.
  4. Unfortunately, xargs -I forces one command per line of input, and there is no option to add trailing parameters, but there is a simple work-round: add the trailing parameters to the input stream.

This is a simplified command with the trailing parameters added (I tested with a different -if condition, not having any PDFs which match):-

{ exiftool -p '${FileName}' -if '$Subject =~/DATA/i' -q *.pdf; \
  echo -e "cat\noutput\nbinder1.pdf"; } | xargs -d'\n' pdftk

The xargs -d'\n' option makes the command work when the file names have embedded blanks.

  • thnx for the elaborate answer, and for pinpointing all the mistakes.. Have some doubts for remark no 3, as exiftool also prints two lines other than file names that have to be thrown out.. – Grigoris L. Jun 22 '17 at 6:16
  • @GrigorisL. - All I can say is that the command works fine for me, and the exiftool command I quoted produces only a single line with the file name. For the record my exiftool revision is 10.10 and I am running Ubuntu 16.04. – AFH Jun 22 '17 at 12:13
  • @ΑFH You are quite right after all. When the -p $ FileName is invoked from the bash, it prints out only file names. When it is invoked from a parameter file, as i did it originally, the output is more perplexed. – Grigoris L. Jun 23 '17 at 6:50
  • OK, but I put ${FileName} as the only line in a file (no comments, headers, etc) and this worked equally well. I used the in-line string for testing and, since my final format was so simple, there seemed no point in putting it in a separate file. By the way, {} are optional in this case: the simpler $FileName is equivalent. – AFH Jun 23 '17 at 11:42
0

The man page for xargs says:

-I replace-str

  Replace occurrences of replace-str in the initial-arguments with
  names read from standard input.   Also, unquoted blanks do not
  terminate input items; instead the separator is the newline
  character.  Implies -x and -L 1.

In other words, you're ending up with a single argument called "20170105170516234.pdf 20170105173126944.pdf 20170105173209758.pdf 20170621163418079.pdf"

I suggest ditching xargs altogether and re-ordering your command something like this:

pdftk $(exiftool -p exifprintformat -if '$Subject =~/DATA/i' -q *.pdf |grep pdf |sed 's/ //g'| tr '\n' ' ') cat output binder1.pdf

This all assumes you don't have spaces in your filenames (safe assumption since you were removing all spaces with sed anyway).

  • thnx! As a newbie, this subtle difference (argument vs arguments) had to be pinpointed to me... Newbie aha moment.. My previous understanding was that xargs called shell -or something- to execute command... Obviously, not so... – Grigoris L. Jun 22 '17 at 6:16

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.