Usando | el carácter de canalización de una variable $ hace que se trate como un argumento más en bash; como escapar

8

Tengo un script bash como este

export pipedargument="| sort -n"
ls $pipedargument

Pero da el error

ls: |: No such file or directory
ls: sort: No such file or directory

Parece estar tratando el contenido de "| sort -n"como un argumento pasado als .

¿Cómo puedo escapar para que se trate como un comando canalizado normal?

Estoy tratando de establecer condicionalmente el $pipedargument. Supongo que podría ejecutar condicionalmente diferentes versiones del comando, pero aún me pregunto si hay una manera de hacer que esto funcione como antes.

laggingreflex
fuente

Respuestas:

9

Tienes razón en que no puedes usar de |esa manera. La razón es que el shell ya ha buscado tuberías y las ha separado en comandos antes de realizar la sustitución de variables. Por lo tanto,| se trata como un personaje más.

Una posible solución es colocar el carácter de la tubería literalmente:

$ cmd="sort -n"
$ ls | $cmd

En el caso de que no desee una canalización, puede usarla catcomo "nop" o marcador de posición:

$ cmd=cat
$ ls | $cmd

Este método evita las sutilezas de eval . Mira también aquí .

Un mejor enfoque: matrices

Un enfoque más sofisticado usaría bashmatrices en lugar de cadenas simples:

$ cmd=(sort -n)
$ ls | "${cmd[@]}"

La ventaja de las matrices se vuelve importante tan pronto como necesite que el comando cmdcontenga argumentos entre comillas.

John1024
fuente
¿Podría aclarar por qué se necesitan matrices en caso de argumentos citados? Me encontré con eso, pero no entiendo por qué no funciona.
anxieux
@anxieux Una respuesta corta es que una cita, cuando está dentro de una cadena de shell, pierde todo su poder sintáctico para agrupar palabras y, en cambio, se trata como cualquier otro carácter. Para una discusión más larga y excelente sobre este tema, vea: Estoy tratando de poner un comando en una variable, ¡pero los casos complejos siempre fallan! .
John1024
5

Puede evalactualizar el comando:

eval "ls $pipedargument"

o incluso mejor definir funciones como:

sorted() { "$@" | sort -n; }

y luego llámalo con los argumentos deseados:

sorted ls /tmp
jimmij
fuente
Otra opción sería definir un alias:alias ls='ls | sort -n'
thiagowfx
0

Yo usaría una función para esto. Algo como:

### usage pipedargument cmd args ###

pipedargument()
{
    sort -n <<< "$( "$@" )"
}

$ pipedargument /sbin/ifconfig eth0
      RX bytes:5904986765 (5.4 GiB)  TX bytes:714370767 (681.2 MiB)
      RX packets:5981427 errors:0 dropped:0 overruns:0 frame:0
      TX packets:4403989 errors:0 dropped:0 overruns:0 carrier:0
      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
      collisions:0 txqueuelen:1000 
      inet addr:XXX.XXX.X.XX  Bcast:XXX.XXX.X.XXX  Mask:255.255.255.0
      inet6 addr: xx00::0x0x:00xx:xx0:000/00 Scope:Link
eth0      Link encap:Ethernet  HWaddr 0x:0x:00:x0:00:00
grepper
fuente