3

I have a bash/shell function that is supposed to find files then awk/copy the first file it finds to another directory. Unfortunately if the directory that contains the file has spaces in the name the whole thing fails, since it truncates the path for some reason or another. How do I fix it?

If file.txt is in /path/to/search/spaces are bad/ it fails.

dir=/path/to/destination/ | find /path/to/search -name file.txt | head -n 1 | awk -v dir="$dir" '{printf "cp \"%s\" \"%s\"\n", $1, dir}' | sh

cp: /path/to/search/spaces: No such file or directory

*If file.txt is in /path/to/search/spacesarebad/ it works, but notice there are no spaces. :-/

3 Answers3

4

Awk's default separator is white space. Simply change it to something else by doing:

awk -F"\t" ...

Your script should look like:

dir=/path/to/destination/ | find /path/to/search -name file.txt | head -n 1 | awk -F"\t" -v dir="$dir" '{printf "cp \"%s\" \"%s\"\n", $1, dir}' | sh

As pointed by the comments, you don't really need all those steps, you could actually simply do (one-liner):

dir=/path/to/destination/ && path="$(find /path/to/search -name file.txt | head -n 1)" && cp "$path" "$dir"

Formated code (that may look better, in this case ^^):

dir=/path/to/destination/
path="$(find /path/to/search -name file.txt | head -n 1)"
cp "$path" "$dir"

The "" are used to assign the entire content of the string to the variable, causing the separator IFS, which is a white space by default, not to be considered over the string.

Rubens
  • 14,478
  • 11
  • 63
  • 92
  • you're the man! thanks very much — I can mark the answer correct in six minutes apparently ;-) – Ned Schneebly Jan 02 '13 at 22:24
  • @NedSchneebly haha! you're welcome, bro'! See my edit and check if that simplification works for you. – Rubens Jan 02 '13 at 22:27
  • for some reason the later two break things as before. This is the output I get with it: `cp: /path/to/search/spaces No such file or directory` `cp: are: No such file or directory` `cp: bad/file.txt: No such file or directory` – Ned Schneebly Jan 02 '13 at 22:43
  • sorry, I forgot you had spaces in the names; embrace the variable with `""` and it shall work. – Rubens Jan 02 '13 at 22:45
  • 1
    heh, i was wondering! if you could update your example with that info it would be good. thanks again for teaching me a few new things today! ~ best regards – Ned Schneebly Jan 02 '13 at 22:47
2

If you think spaces are bad, wait till you get into trouble with newlines. Consider for example:

mkdir spaces\ are\ bad
touch spaces\ are\ bad/file.txt
mkdir newlines$'\n'are$'\n'even$'\n'worse
touch newlines$'\n'are$'\n'even$'\n'worse/file.txt

And:

find . -name file.txt                                   

The head command assumes newline delimiter. You can get around the space and newline issue with GNU find and GNU grep (maybe others) by using \0 delimiters:

find . -name file.txt -print0 | grep -zm1 . | xargs -0 cp -t "$dir"
Thor
  • 45,082
  • 11
  • 119
  • 130
  • wow — great stuff; thanks for the excellent example. I'm now scratching my head trying to decide which method to use! :-p – Ned Schneebly Jan 02 '13 at 22:41
0

You could try this.

awk '{print substr($0, index($0,$9))}'

For example this is the output of ls command:

-rw-r--r--. 1 root root 73834496 Dec 6 10:55 File with spaces 2

If you use simple awk like this

# awk '{print $9}'

It returns only

# File

If used with the full command

# awk '{print substr($0, index($0,$9))}'

I get the whole output

File with spaces 2

Here substr(s, a, b) : it returns b number of chars from string s, starting at position a. The parameter b is optional.

For example if the match is addr:192.168.1.133 and you use substr as follows

# awk '{print substr($2,6)}'

You get the IP i.e 192.168.1.133. Note the 6 is the character starting from a in addr

So in the proper command the $2 is $0 ( which prints whole line.) and index($0,$9) matches $9 and prints everything ahead of column 9. You can change that to index($0,$8) and see that the output changes to

# 10:55 File with spaces 2

`index(IN, FIND)' This searches the string IN for the first occurrence of the string FIND, and returns the position in characters where that occurrence begins in the string IN.

I hope it helps. Moreover if you are assigning this value to a variable in script then you need to enclose the variables in double quotes. Other wise you will get errors if you are doing some other operation for the extracted file name.

Himanshu Chauhan
  • 812
  • 9
  • 11