Reputation: 169
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.
Upvotes: 7
Views: 11343
Reputation: 11
I would do:
find . -iname "filename" -exec tail -n 2 {} \; > output.txt
Upvotes: 0
Reputation: 58928
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:
find
files in the current directory (.
) and below with a name containing foobar
(case insensitive because of the i
in -iname
);-exec
) a command to{}
) followed by a NUL character (\0
) individually (\;
);tr '\0\n' '\n\0'
);"tail -n 2
, "$@"
);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
).
Upvotes: 5
Reputation: 107080
Robin Green:
$ find . -iname "*FooBar*"|tail -n2|xargs -i cp "{}" dest
Unfortunately this won't work with filenames that contain spaces or newlines.
This will work (at least to the tail
) if the file contains spaces. That's because the find will put each file on one line including spaces, tabs, and other special characters.
The problem is that xargs
will not operate with spaces. You can use the -0
or --null
option with xargs
, but that was designed with find ... -print0
in mind.
What might work is using a while
loop.
find . -iname "*FooBar*" | tail -n2 | while read file
do
cp "$file" "$dest"
done
Since you're only reading in one item per line, the $file
will contain the file name with all of the various characters. The only time this will not work is if $file
contains a NL. Then, the tail
command itself will have issues. Fortunately, having a NL in a file name is quite rare.
Some people do this:
while IFS=\n read file
which removes input separators on anything other than a NL, but that shouldn't be necessary.
Upvotes: 2
Reputation: 1952
Since You file contains 2 lines also have spaces lets keep inverted commas
while read line
do
cp "$line" dest;
done < $(find . -iname "*FooBar*" | tail -2)
Upvotes: 1
Reputation: 33083
find . -iname "*FooBar*"|tail -n2|xargs -i cp "{}" dest
Unfortunately this won't work with filenames that contain spaces or newlines.
Upvotes: 2