Bash Command Line – How to Edit a List of Generated Files with Spaces in Names

bashcommand linevixargs

I've got a relatively small list of filenames generated from a pipeline based on find. The file names contain spaces and possibly punctuation but definitely no other non-printing characters or newlines.

For example,

Netherlands/Purge (GDPR) 2020-01-09.txt
Netherlands/Purge (GDPR) 2020-01-27.txt
Switzerland/New mailing 2020-01-27.txt

I want to edit these files as a set (vi file1 file2 file3 rather than vi file1; vi file2; vi file3), partly so that I can easily jump forwards and backwards between them.

I've started with Using a generated list of filenames as argument list — with spaces
, which has a standard find -print0 | xargs -0 mycommand solution. Unfortunately this does not work when mycommand is an editor because although xargs can assemble the set of files to edit, stdin is already taken up from the pipeline and I can't see a way to run an editor in-place. I can't use find -exec vi {} + because I'm using a pipeline to validate the set of filenames, and not just find itself.

My other option is to copy and paste, assembling the list of file names, surrounding them with quotes, and then prefixing the result with vi. For these three files it's trivial, but in the general case it's not an easily-reusable solution,

vi 'Netherlands/Purge (GDPR) 2020-01-09.txt' 'Netherlands/Purge (GDPR) 2020-01-27.txt' 'Switzerland/New mailing 2020-01-27.txt'

Given a GNU/Linux platform with bash as my preferred shell (in case it matters), how can I edit a similarly generated list of files?

Best Answer

From the comments I get something similar like this is your command:

find -type f -mtime +14 -mtime -22 -iname '*.xml' | while IFS= read -f x; do xmlstarlet sel -T -t -v '//magicElement' -n "$x" | grep -q magicValue && echo "$x"; done 

Instead of piping to a while - loop you could use -exec sh -c '...' to filter files:

find -type f -mtime +14 -mtime -22 -iname '*.xml' \
  -exec sh -c 'xmlstarlet sel -T -t -v "//magicElement" "$1" | grep -q magicValue' find-sh {} \; \
  -exec vi -- {} +

Try:

Consider three files:

.
├── a:<magicElement>magicValue</magicElement>
├── b:<magicElement>magicValue</magicElement>
└── c:<magicElement>someOtherValue</magicElement>

$ find . -type f \
  -exec sh -c 'xmlstarlet sel -T -t -v "//magicElement" "$1" | grep -q magicValue' find-sh {} \; \
  -exec echo vi -- {} +

Output:

vi -- ./a ./b
Related Question