Как мы можем запустить команду, хранящуюся в переменной?

  1. Используйте команду mount и проверьте, где смонтирован раздел Windows C .
  2. cd / media / XXXXXX , где XXXX - упомянутая точка монтирования.
  3. Теперь cd Windows / System32 / config /
40
04.09.2019, 13:08
6 ответов

Если это не работает с '', вам следует использовать``:

abc=`ls -l /tmp/test/my\ dir/`

Обновление лучше:

abc=$(ls -l /tmp/test/my\ dir/)
-2
28.04.2021, 23:46

Esto se ha discutido en varias preguntas sobre unix.SE, trataré de recopilar todos los problemas que se me ocurran aquí. A continuación se muestra una descripción de por qué y cómo fallan los diversos intentos, una forma de hacerlo correctamente con una función (para un comando fijo ), o con arreglos de shell (Bash/ksh/zsh )o el $@pseudo -array (POSIX sh ), y notas sobre el uso de evalpara hacer esto. Algunas referencias al final.


Por qué falla

La razón por la que enfrenta esos problemas es la división de palabras y el hecho de que las comillas expandidas de variables no actúan como comillas, sino que son solo caracteres comunes.

(Tenga en cuenta que esto es similar a cualquier otro lenguaje de programación :p. char *s = "foo()"; printf("%s\n", s)no llama a la función foo()en C, sino que simplemente imprime la cadena foo(). El shell es un lenguaje de programación, no un procesador de macros.)

Los casos presentados en la pregunta:

La asignación aquí asigna la cadena única ls -l "/tmp/test/my dir"aabc:

$ abc='ls -l "/tmp/test/my dir"'

A continuación, $abcse divide en espacios en blanco, y lsobtiene los tres argumentos -l, "/tmp/test/myydir"(con una comilla al principio del segundo y otra al final del tercero ). La opción funciona, pero la ruta se procesa incorrectamente:

$ $abc
ls: cannot access '"/tmp/test/my': No such file or directory
ls: cannot access 'dir"': No such file or directory

Aquí, se cita la expansión, por lo que se mantiene como una sola palabra. El shell intenta encontrar un programa llamado literalmente ls -l "/tmp/test/my dir", espacios y comillas incluidos.

$ "$abc"
bash: ls -l "/tmp/test/my dir": No such file or directory

Y aquí, $abcse divide, y solo la primera palabra resultante se toma como argumento para -c, por lo que Bash simplemente ejecuta lsen el directorio actual. Las otras palabras son argumentos para bash, y se usan para completar $0, $1, etc.

$ bash -c $abc
'my dir'

Con bash -c "$abc"y eval "$abc", hay un paso de procesamiento de shell adicional, que hace que las comillas funcionen, pero también hace que todas las expansiones de shell se procesen de nuevo , por lo que existe el riesgo de que accidentalmente corriendo por ejemplo una sustitución de comando del usuario -proporcionó datos, a menos que tenga mucho cuidado al citar.


Mejores formas de hacerlo

Las dos mejores formas de almacenar un comando son a )usar una función en su lugar, b )usar una variable de matriz (o los parámetros posicionales ).

Uso de una función:

Simplemente declare una función con el comando dentro y ejecute la función como si fuera un comando.Las expansiones en los comandos dentro de la función solo se procesan cuando se ejecuta el comando, no cuando se define, y no necesita citar los comandos individuales.

# define it
myls() {
    ls -l "/tmp/test/my dir"
}

# run it
myls

Usando una matriz:

Las matrices permiten crear variables de múltiples -palabras donde las palabras individuales contienen espacios en blanco. Aquí, las palabras individuales se almacenan como elementos de matriz distintos, y la expansión "${array[@]}"expande cada elemento como palabras de shell separadas:

# define the array
mycmd=(ls -l "/tmp/test/my dir")

# run the command
"${mycmd[@]}"

La sintaxis es un poco horrible, pero las matrices también le permiten construir la línea de comando pieza -por pieza -. Por ejemplo:

mycmd=(ls)               # initial command
if [ "$want_detail" = 1 ]; then
    mycmd+=(-l)          # optional flag
fi
mycmd+=("$targetdir")    # the filename

"${mycmd[@]}"

o mantenga partes de la línea de comando constantes y use la matriz para completar solo una parte, como opciones o nombres de archivo:

options=(-x -v)
files=(file1 "file name with whitespace")
target=/somedir

transmutate "${options[@]}" "${files[@]}" "$target"

La ​​desventaja de los arreglos es que no son una función estándar, por lo que los shells POSIX simples (como dash, el predeterminado /bin/shen Debian/Ubuntu )no los admiten (pero ver abajo ). Sin embargo, Bash, ksh y zsh sí, por lo que es probable que su sistema tenga algún shell que admita matrices.

Usando"$@"

En shells sin soporte para matrices con nombre, aún se pueden usar los parámetros posicionales (la pseudo -matriz"$@")para contener los argumentos de un comando.

Los siguientes deben ser bits de script portátiles que hagan el equivalente a los bits de código de la sección anterior. La matriz se reemplaza con "$@", la lista de parámetros posicionales. La configuración "$@"se realiza con set, y las comillas dobles alrededor de "$@"son ​​importantes (, ya que hacen que los elementos de la lista se cite individualmente ).

Primero, simplemente almacenar un comando con argumentos en "$@"y ejecutarlo:

set -- ls -l "/tmp/test/my dir"
"$@"

Configuración condicional de partes de las opciones de línea de comando para un comando:

set -- ls
if [ "$want_detail" = 1 ]; then
    set -- "$@" -l
fi
set -- "$@" "$targetdir"

"$@"

Solo usando "$@"para opciones y operandos:

set -- -x -v
set -- "$@" file1 "file name with whitespace"
set -- "$@" /somedir

transmutate "$@"

(Por supuesto, "$@"generalmente se completa con los argumentos del guión en sí,así que tendrás que guardarlos en algún lugar antes de re -proponer "$@".)


¡Usandoeval(tenga cuidado aquí!)

evaltoma una cadena y la ejecuta como un comando, como si se hubiera ingresado en la línea de comando del shell. Esto incluye todo el proceso de cotización y expansión, que es a la vez útil y peligroso.

En el caso simple, permite hacer justo lo que queremos:

cmd='ls -l "/tmp/test/my dir"'
eval "$cmd"

Con eval, las comillas se procesan, por lo que lsfinalmente ve solo los dos argumentos -ly /tmp/test/my dir, como queremos. evaltambién es lo suficientemente inteligente como para concatenar cualquier argumento que obtenga, por lo que eval $cmdtambién podría funcionar en algunos casos, pero p. todas las ejecuciones de espacios en blanco se cambiarían a espacios individuales. Todavía es mejor citar la variable allí, ya que eso asegurará que no se modifique a eval.

Sin embargo, es peligroso incluir la entrada del usuario en la cadena de comando paraeval. Por ejemplo, esto parece funcionar:

read -r filename
cmd="ls -ld '$filename'"
eval "$cmd";

¡Pero si el usuario proporciona una entrada que contiene comillas simples, puede romper las comillas y ejecutar comandos arbitrarios! Por ej. con la entrada '$(whatever)'.txt, su secuencia de comandos ejecuta felizmente la sustitución del comando. Que podría haber sidorm -rf(o peor )en su lugar.

El problema es que el valor de $filenamese incrustó en la línea de comando que ejecuta eval. Se amplió antes de eval, que vio, p. el comando ls -l ''$(whatever)'.txt'. Necesitará procesar previamente -la entrada para estar seguro.

Si lo hacemos al revés, manteniendo el nombre del archivo en la variable, y dejando que el comando evallo expanda, es más seguro de nuevo:

read -r filename
cmd='ls -ld "$filename"'
eval "$cmd";

Tenga en cuenta que las comillas externas ahora son comillas simples, por lo que no se producen expansiones internas. Por lo tanto, evalve el comando ls -l "$filename"y expande el nombre del archivo de forma segura.

Pero eso no es muy diferente de simplemente almacenar el comando en una función o una matriz. Con funciones o arreglos,no existe tal problema ya que las palabras se mantienen separadas todo el tiempo, y no hay comillas u otro procesamiento para el contenido de filename.

read -r filename
cmd=(ls -ld -- "$filename")
"${cmd[@]}"

Prácticamente, la única razón para usar evales una en la que la parte variable involucra elementos de sintaxis de shell que no se pueden traer a través de variables (canalizaciones, redirecciones, etc. ). Sin embargo, luego deberá citar/escapar todo más en la línea de comando que necesita protección del paso de análisis adicional (consulte el enlace a continuación ). En cualquier caso, ¡es mejor evitar incrustar la entrada del usuario en el comando eval!


Referencias

165
20.08.2021, 11:18

El segundo signo de comillas rompe el comando.

Cuando corro:

abc="ls -l '/home/wattana/Desktop'"
$abc

Me dio un error.

Pero cuando corro

abc="ls -l /home/wattana/Desktop"
$abc

No hay ningún error

No hay forma de arreglar esto en este momento (para mí )pero puede evitar el error al no tener espacio en el nombre del directorio.

Esta respuesta dijo que el comando eval se puede usar para solucionar esto, pero no funciona para mí:(

2
20.08.2021, 11:18

La forma más segura de ejecutar un comando (no -trivial )es eval. Luego puede escribir el comando como lo haría en la línea de comando y se ejecuta exactamente como si lo hubiera ingresado. Pero tienes que citar todo.

Caso sencillo:

abc='ls -l "/tmp/test/my dir"'
eval "$abc"

caso no tan simple:

# command: awk '! a[$0]++ { print "foo: " $0; }' inputfile
abc='awk '\''! a[$0]++ { print "foo: " $0; }'\'' inputfile'
eval "$abc"
13
20.08.2021, 11:18

Как насчет лайнера python3 one -?

bash-4.4# pwd
/home
bash-4.4# CUSTOM="ls -altr"
bash-4.4# python3 -c "import subprocess; subprocess.call(\"$CUSTOM\", shell=True)"
total 8
drwxr-xr-x    2 root     root          4096 Mar  4  2019.
drwxr-xr-x    1 root     root          4096 Nov 27 16:42..
-2
20.08.2021, 11:18

Еще один простой прием для запуска любой (тривиальной/не -тривиальной )команды, хранящейся в abcпеременной,:

$ history -s $abc

и нажмите UpArrowили Ctrl-p, чтобы вывести его в командную строку. В отличие от любого другого метода, вы можете отредактировать его перед выполнением, если это необходимо.

Эта команда добавит содержимое переменной в качестве новой записи в историю Bash, и вы сможете отозвать ее, нажав UpArrow.

-2
20.08.2021, 11:18

Теги

Похожие вопросы