开发者

How to execute multiple commands after xargs -0?

开发者 https://www.devze.com 2023-04-05 17:17 出处:网络
find . -name \"filename including space\" -print0 | xargs -0 ls -aldF >开发者_开发知识库 log.txt
find . -name "filename including space" -print0 | xargs -0 ls -aldF >开发者_开发知识库 log.txt
find . -name "filename including space" -print0 | xargs -0 rm -rdf

Is it possible to combine these two commands into one so that only 1 find will be done instead of 2?

I know for xargs -I there may be ways to do it, which may lead to errors when proceeding filenames including spaces. Any guidance is much appreciated.


find . -name "filename including space" -print0 | 
  xargs -0 -I '{}' sh -c 'ls -aldF {} >> log.txt; rm -rdf {}'

Ran across this just now, and we can invoke the shell less often:

find . -name "filename including space" -print0 | 
  xargs -0 sh -c '
      for file; do
          ls -aldF "$file" >> log.txt
          rm -rdf "$file"
      done
  ' sh

The trailing "sh" becomes $0 in the shell. xargs provides the files (returrned from find) as command line parameters to the shell: we iterate over them with the for loop.


If you're just wanting to avoid doing the find multiple times, you could do a tee right after the find, saving the find output to a file, then executing the lines as:

find . -name "filename including space" -print0 | tee my_teed_file | xargs -0 ls -aldF > log.txt
cat my_teed_file | xargs -0 rm -rdf 

Another way to accomplish this same thing (if indeed it's what you're wanting to accomplish), is to store the output of the find in a variable (supposing it's not TB of data):

founddata=`find . -name "filename including space" -print0`
echo "$founddata" | xargs -0 ls -aldF > log.txt
echo "$founddata" | xargs -0 rm -rdf


I believe all these answers by now have given out the right ways to solute this problem. And I tried the 2 solutions of Jonathan and the way of Glenn, all of which worked great on my Mac OS X. The method of mouviciel did not work on my OS maybe due to some configuration reasons. And I think it's similar to Jonathan's second method (I may be wrong).

As mentioned in the comments to Glenn's method, a little tweak is needed. So here is the command I tried which worked perfectly FYI:

find . -name "filename including space" -print0 | 
xargs -0 -I '{}' sh -c 'ls -aldF {} | tee -a log.txt ; rm -rdf {}'

Or better as suggested by Glenn:

find . -name "filename including space" -print0 | 
xargs -0 -I '{}' sh -c 'ls -aldF {} >> log.txt ; rm -rdf {}'


As long as you do not have newline in your filenames, you do not need -print0 for GNU Parallel:

find . -name "My brother's 12\" records" | parallel ls {}\; rm -rdf {} >log.txt

Watch the intro video to learn more: http://www.youtube.com/watch?v=OpaiGYxkSuQ


Just a variation of the xargs approach without that horrible -print0 and xargs -0, this is how I would do it:

ls -1 *.txt  | xargs --delimiter "\n" --max-args 1 --replace={} sh -c 'cat {}; echo "\n"'

Footnotes:

  • Yes I know newlines can appear in filenames but who in their right minds would do that
  • There are short options for xargs but for the reader's understanding I've used the long ones.
  • I would use ls -1 when I want non-recursive behavior rather than find -maxdepth 1 -iname "*.txt" which is a bit more verbose.


You can execute multiple commands after find using for instead of xargs:

IFS=$'\n'
for F in `find . -name "filename including space"`
do
    ls -aldF $F > log.txt
    rm -rdf $F
done

The IFS defines the Internal Field Separator, which defaults to <space><tab><newline>. If your filenames may contain spaces, it is better to redefine it as above.


I'm late to the party, but there is one more solution that wasn't covered here: user-defined functions. Putting multiple instructions on one line is unwieldy, and can be hard to read/maintain. The for loop above avoids that, but there is the possibility of exceeding the command line length.

Here's another way (untested).

function processFiles {
  ls -aldF "$@"
  rm -rdf "$@"
}
export -f processFiles

find . -name "filename including space"` -print0 \
  | xargs -0 bash -c processFiles dummyArg > log.txt

This is pretty straightforward except for the "dummyArg" which gave me plenty of grief. When running bash in this way, the arguments are read into

"$0" "$1" "$2"  ....

instead of the expected

"$1" "$2" "$3"    ....

Since processFiles{} is expecting the first argument to be "$1", we have to insert a dummy value into "$0".

Footnontes:

  1. I am using some elements of bash syntax (e.g. "export -f"), but I believe this will adapt to other shells.
  2. The first time I tried this, I didn't add a dummy argument. Instead I added "$0" to the argument lines inside my function ( e.g. ls -aldf "$0" "$@" ). Bad idea. Aside from stylistic issues, it breaks when the "find" command returns nothing. In that case, $0 is set to "bash", Using the dummy argument instead avoids all of this.


Another solution:

find . -name "filename including space" -print0 \
| xargs -0 -I FOUND echo "$(ls -aldF FOUND > log.txt ; rm -rdf FOUND)"
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号