Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Piping `find` to 'tail`

Tags:

linux

bash

pipe

I want to get the last two lines of the find output and copy them somewhere. I tried

find . -iname "*FooBar*" | tail -2 -exec cp "{}" dest \;

but the output was "invalid option --2" for tail.

Also, my file or directory name contains spaces.

like image 622
Hari K Avatar asked Jan 05 '14 16:01

Hari K


2 Answers

The following should work on absolutely any paths.

Declare a function to be able to use head and tail on NUL-separated output:

nul_terminated() {
    tr '\0\n' '\n\0' | "$@" | tr '\0\n' '\n\0'
}

Then you can use it to get a NUL-separated list of paths from your search after passing through tail:

find . -exec printf '%s\0' {} \; | nul_terminated tail -n 2

You can then pipe that to xargs and add your options:

find . -iname "*FooBar*" -exec printf '%s\0' {} \; | nul_terminated tail -n 2 | xargs -I "{}" -0 cp "{}" "dest"

Explanation:

  1. find files in the current directory (.) and below with a name containing foobar (case insensitive because of the i in -iname);
  2. for each file, run (-exec) a command to
  3. print each file path ({}) followed by a NUL character (\0) individually (\;);
  4. swap newlines and NUL characters (tr '\0\n' '\n\0');"
  5. get the last two lines (that is, paths; tail -n 2, "$@");
  6. swap newlines and NUL characters again to get a NUL-separated list of file names (tr '\0\n' '\n\0').

The xargs command is a bit harder to explain. It builds as many cp ... "dest" commands as necessary to fit in the maximum command length of the operating system, replacing the {} token in the command with the actual file name (-I "{}" ... "{}"), using the NUL character as a separator when reading the parameters (-0).

like image 75
l0b0 Avatar answered Oct 05 '22 08:10

l0b0


You can try

cp $(find . -iname "*FooBar*" | tail -2 ) dest
like image 38
gturri Avatar answered Oct 05 '22 06:10

gturri