Using Xargs with Special Characters

How can I use xargs to copy files that have spaces and quotes in their names?

You can combine all of that into a single find command:

find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;

This will handle filenames and directories with spaces in them. You can use -name to get case-sensitive results.

Note: The -- flag passed to cp prevents it from processing files starting with - as options.

How to return length of string in xargs (special characters)

The problem is not xargs. The problem is sh. If you want the correct (unicode) length, you need to use a shell that supports unicode. Observe:

$ echo $a | xargs -rL1 sh -c 'echo ${#0}'
4
$ echo $a | xargs -rL1 bash -c 'echo ${#0}'
3

On debian-like systems, the default shell, /bin/sh, is actually dash which does not understand unicode. As shown above, simply replacing sh with bash solves the problem.

Escape character ' inside xargs

You may try this xargs:

xargs -n1 -P8 bash -c "url=\"\$0\"; \
curl -ks -x http://127.0.0.1:8080 -A \"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'\" \$url -m 4 1>/dev/null" {} < webs.txt

I'm using xargs, but the argument list is too long

You don't need xargs here, you can do

find . -type f -exec dos2unix '{}' +

Add escapes to special characters in a string (as a function)

Before I attempt to answer, I have a some warnings. I'm not sure what the actual goal is here, so depending on what that is, there are several potential problems.

First, it's impossible in general to reconstruct how a string was quoted/escaped on the command line, because there are many different ways to express the same string in shell syntax. For example, all of the following commands pass exactly the same argument to echo, and therefore print exactly the same thing:

echo This\ is\ a\ special\ character\ string.\ \\\~\`\!\@\#\$\%\^\&\*\(\)\-\=\+\[\]\{\}\|\;\:\'\"\,\<\>\/\?
echo This\ is\ a\ special\ character\ string.\ \\~\`\!@#\$%^\&*\(\)-=+[]{}\|\;:\'\",\<\>/?
echo 'This is a special character string. \~`!@#$%^&*()-=+[]{}|;:'"'"'",<>/?'
echo $'This is a special character string. \~`!@#$%^&*()-=+[]{}|;:\'",<>/?'
...and many more

(Note: technically, the second of those might pass something different, if the current directory happens to contain one or more files with specific really weird names.)

Second, some versions of echo do some additional escape processing. Some do this only when passed the -e option. Some print "-e" if you try to pass the -e option. It's a mess.

Third, what needs to be escaped and how really depends on what you're going to use it for (and specifically, how it's going to be parsed). Different situations involve different parsing rules, and you have to add escapes appropriate for the specific processing that the output is going to be subject to. In my answer, I concentrated on reversing the specific escaping in your example.

My solution: you can use sed in a pipeline to add escapes before any of a list of characters, specified as a bracket expression. It's slightly tricky because "]" and "-" are delimiters in a bracket expression; the trick there is to specify "]" as the first character and "-" as the last, so they're not mistaken for their other meanings. Also, I'm going to write this as a single-quoted string, so the single-quote requires special handing. Like this:

sed 's/[][ \~`!@#$%^&*()=+{}|;:'"'"'",<>/?-]/\\&/g'

Or as a function:

addESC() { sed 's/[][ \~`!@#$%^&*()=+{}|;:'"'"'",<>/?-]/\\&/g'; }

Example:

$ echo This\ is\ a\ special\ character\ string.\ \\\~\`\!\@\#\$\%\^\&\*\(\)\-\=\+\[\]\{\}\|\;\:\'\"\,\<\>\/\? | addESC 
This\ is\ a\ special\ character\ string.\ \\\~\`\!\@\#\$\%\^\&\*\(\)\-\=\+\[\]\{\}\|\;\:\'\"\,\<\>\/\?

As for why your attempts didn't work: In the first, printf doesn't read from stdin, it expects arguments. The third tries to fix this with xargs, but xargs does its own quote/escape parsing and removal, which messes it up. In the second, <<< takes a string, not a command; to apply it to the output of a command, you'd use something like <<< "$(command)". Also, in all versions, bash printf's %q quotes and/or escapes specifically as needed for consumption by bash itself, which doesn't match the escaping in your example.

use whitespace as delimiter for xargs arguments

You could use the -d option to specify the delimiter:

echo 'foo bar' | xargs -I {} -d ' ' sh -c 'echo item: {}'

Will result in:

item: foo
item: bar

--delimiter=delim, -d delim

Input items are terminated by the specified character. The specified delimiter may be a single character, a C-style character escape such as \n, or an octal or hexadecimal escape code. Octal and
hexadecimal escape codes are understood as for the printf command. Multibyte characters are not supported. When processing the input, quotes and backslash are not special; every character in the
input is taken literally. The -d option disables any end-of-file string, which is treated like any other argument. You can use this option when the input consists of simply newline-separated items,
although it is almost always better to design your program to use --null where this is possible.

Make xargs handle filenames that contain spaces

The xargs command takes white space characters (tabs, spaces, new lines) as delimiters.

You can narrow it down only for the new line characters ('\n') with -d option like this:

ls *.mp3 | xargs -d '\n' mplayer

It works only with GNU xargs.

For MacOS:

ls *.mp3 | tr \\n \\0 | xargs -0 mplayer

The more simplistic and practically useful approach (when don't need to process the filenames further):

mplayer *.mp3


Related Topics



Leave a reply



Submit