2

I'm looking for a standard tool capable of taking all of its arguments and turning it into a single string suitable for use as multiple arguments in an automatically generated bash/sh/zsh script. Such a command is extremely useful in various disciplines of script-fu. An example of its usage:

% shsafe 'A big \nasty string '\'' $HOME $PATH' 'another string \\'
'A big \nasty string '\'' $HOME $PATH' 'another string \\'

Using it in another script:

% sshc host rm 'file/with spaces and $special chars'

where sshc contains

#!/bin/bash
# usage: sshc host command [arg ...]
# Escapes its arguments so that the command may contain special 
# characters. Assumes the remote shell is sh-like.
host=$1
shift
exec ssh "$host" "$(shsafe "$@")"

Another example:

#!/bin/bash
# Run multiple commands in a single sudo session. The arguments of 
# this script are passed as arguments to the first command. Useful if 
# you don't want to have to type the password for both commands and 
# the first one takes a while to run.
sudo bash -c "pacman -Syu $(shsafe "$@") && find /etc -name '*.pacnew'"

I couldn't find a suitable solution to this problem in the pre-existing commands, so I made up my own, called shsafe. It uses the fact that single quotes, '', turn off absolutely all shell expansion, except for ' itself.

shsafe:

#!/usr/bin/env python

from sys import *

n = len(argv)
if n == 1:
    exit(0)

i = 1
while True:
    stdout.write("'" + argv[i].replace("'", "'\\''") + "'")
    i += 1
    if i == n:
        break
    stdout.write(' ')

stdout.write('\n')

Is there any standard tool capable of doing this to its arguments?

Note that the printf command with a format string consisting of just the %q formatter is not good enough for this, because it won't keep multiple arguments separated:

% printf %q arg1 arg2
arg1arg2
enigmaticPhysicist
  • 1,518
  • 16
  • 21
  • `printf "%q " arg1 arg2` ? `printf "%q" "arg1 arg2"` ? – 123 Nov 07 '16 at 09:40
  • I thought of that. Even with the space, arg1 and arg2 will be treated as the same argument, separated by a space. `printf '$'\''%q'\'' ' arg1 arg2` does work, though. It's extremely error prone, though, having to type that out every time. I don't consider that a good enough solution because of that, even if it did only use standard tools. – enigmaticPhysicist Nov 07 '16 at 09:49
  • Do you mean `$'\'%q\' '` ? How do you want the string to look when it is printed? – 123 Nov 07 '16 at 09:55
  • isn't `xargs` solution to your problem? – nopasara Nov 07 '16 at 10:01
  • No, that's something different, @nopasara. `man xargs` to learn what it does. – enigmaticPhysicist Nov 07 '16 at 10:09

1 Answers1

1

I did eventually figure out a decent way of doing this:

% printf "$'%q' " 'crazy string \ $HOME' 'another\ string'
$'crazy\ string\ \\\ \$HOME' $'another\\\ string'

It's a little error prone what with the quotes everywhere, so it's not ideal, IMO, but it's a solid solution that should work anywhere. If it's being used a lot, you could always turn it into a shell function:

shsafe () {
    printf "$'%q' " "$@"
}
enigmaticPhysicist
  • 1,518
  • 16
  • 21