¿Cómo puedo escapar una cadena para un comando de shell en node?


En nodejs, la única forma de ejecutar comandos externos es a través de sys.exec (cmd). Me gustaría llamar a un comando externo y darle datos a través de stdin. En nodejs todavía no parece haber una manera de abrir un comando y luego enviar datos a él (solo para exec y recibir sus salidas de error estándar+), por lo que parece que la única manera que tengo que hacer esto ahora es a través de un solo comando de cadena como:

var dangerStr = "bad stuff here";
sys.exec("echo '" + dangerStr + "' | somecommand");

La mayoría de las respuestas a preguntas como esta se han centrado en cualquiera de las expresiones regulares que no trabaja para mí en nodejs (que utiliza el motor Javascript V8 de Google) o funciones nativas de otros lenguajes como Python.

Me gustaría escapar de dangerStr para que sea seguro componer una cadena exec como la anterior. Si ayuda, dangerStr contendrá datos JSON.

Author: mikemaccana, 2009-11-22

5 answers

Hay una manera de escribir en un comando externo: process.createChildProcess (documentation ) devuelve un objeto con un método write. createChildProcess no es tan conveniente, sin embargo, porque no almacena en búfer stdout y stderr, por lo que necesitará controladores de eventos para leer la salida en trozos.

var stdout = "", stderr = "";
var child = process.createChildProcess("someCommand");

child.addListener("output", function (data) {
    if (data !== null) {
        stdout += data;
    }
});
child.addListener("error", function (data) {
    if (data !== null) {
        stderr += data;
    }
});
child.addListener("exit", function (code) {
    if (code === 0) {
        sys.puts(stdout);
    }
    else {
        // error
    }
});

child.write("This goes to someCommand's stdin.");
 3
Author: Matthew Crumley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-09 21:14:09

Esto es lo que uso:

var escapeShell = function(cmd) {
  return '"'+cmd.replace(/(["\s'$`\\])/g,'\\$1')+'"';
};
 33
Author: Sylvain Zimmer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-10 19:45:14

Si necesita una solución simple, puede usar esto:

function escapeShellArg (arg) {
    return `'${arg.replace(/'/g, `'\\''`)}'`;
}

Así que tu cadena simplemente se escapará con comillas simples como mencionó Chris Johnsen.

echo 'John'\''s phone';

Funciona en bash porque a fuerte citando, se siente como que también funciona en fish, pero no funciona en zsh y sh.

Si tienes bash puedes ejecutar tu script en sh o zsh con 'bash -c \'' + escape('all-the-rest-escaped') + '\''.

Pero en realidad... nodo.js escapará de todos los personajes necesarios para usted:

var child = require('child_process')
  .spawn('echo', ['`echo 1`;"echo $SSH_TTY;\'\\0{0..5}']);

child.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

child.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

Este bloque de código se ejecutará:

echo '`echo 1`;"echo $SSH_TTY;'\''\\0{0..5}'

Y producirá:

stdout: `echo 1`;"echo $SSH_TTY;\'\\0{0..5}

O algún error.

Echa un vistazo a http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

Por cierto, la solución simple para ejecutar un montón de comandos es:

require('child_process')
  .spawn('sh', ['-c', [
    'cd all/your/commands',
    'ls here',
    'echo "and even" > more'
  ].join('; ')]);

Que tenga un buen día!

 13
Author: Alex Yaroshevich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-02 23:14:05

Usted debe nunca confiar en escapar de la entrada desconocida que va a un parámetro de shell - casi siempre habrá algún caso de borde que no haya pensado que permite al usuario ejecutar código arbitrario en su servidor.

Node tiene soporte para llamar a un comando y pasar cada argumento por separado, sin necesidad de escapar. Esta es la forma más segura de hacerlo:

const { spawn } = require('child_process');
// Note that the arguments are in an array, not using string interpolation
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.log(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

La documentación está aquí

 2
Author: Will Richardson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-19 11:50:47

Si también necesita tratar con caracteres especiales (saltos de línea, etc.) puedes hacerlo de esta manera:

str = JSON.stringify(str)
    .replace(/^"|"$/g,'') //remove JSON-string double quotes
    .replace(/'/g, '\'"\'"\'') //escape single quotes the ugly bash way

Esto supone que se usa la comilla fuerte de Bash a través de comillas simples) y el receptor puede entender el escape tipo C de JSON.

 -2
Author: MicMro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-04 06:25:28