Utilisation | le caractère pipe à partir d'une variable $ le fait traiter comme un autre argument dans bash; comment y échapper?

8

J'ai un script bash comme celui-ci

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

Mais ça donne l'erreur

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

Il semble traiter le contenu de "| sort -n"comme un simple argument transmis ls.

Comment puis-je y échapper afin qu'il soit traité comme une commande régulière canalisée?

J'essaie de régler conditionnellement le $pipedargument. Je suppose que je pourrais simplement exécuter conditionnellement différentes versions de la commande, mais je me demande toujours s'il existe un moyen de faire fonctionner cela comme ci-dessus?

laggingreflex
la source

Réponses:

9

Vous avez raison de ne pas pouvoir utiliser de |cette façon. La raison en est que le shell a déjà recherché des pipelines et les a séparés en commandes avant d'effectuer la substitution de variable. Par conséquent, |est traité comme un autre caractère.

Une solution de contournement possible consiste à placer le caractère de canal littéralement:

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

Dans le cas où vous ne voulez pas de pipeline, vous pouvez utiliser catcomme "nop" ou espace réservé:

$ cmd=cat
$ ls | $cmd

Cette méthode évite les subtilités de l'évaluation . Voir aussi ici .

Une meilleure approche: les tableaux

Une approche plus sophistiquée utiliserait des bashtableaux à la place de chaînes simples:

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

L'avantage des tableaux devient important dès que vous avez besoin de la commande cmdpour contenir des arguments entre guillemets.

John1024
la source
Pourriez-vous préciser pourquoi les tableaux sont nécessaires en cas d'arguments cités? J'ai rencontré cela, mais je ne comprends pas très bien pourquoi cela ne fonctionne pas.
anxieux
@anxieux Une réponse courte est qu'une citation, lorsqu'elle est à l' intérieur d' une chaîne de shell, perd tout son pouvoir syntaxique pour grouper les mots et est plutôt traitée comme n'importe quel autre caractère. Pour une discussion plus longue et excellente de ce problème, voir: J'essaie de mettre une commande dans une variable, mais les cas complexes échouent toujours! .
John1024
5

Vous pouvez evalutiliser la commande:

eval "ls $pipedargument"

ou encore mieux définir une fonction comme:

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

et plus tard, appelez-le avec les arguments souhaités:

sorted ls /tmp
jimmij
la source
Une autre option serait de définir un alias:alias ls='ls | sort -n'
thiagowfx
0

J'utiliserais une fonction pour cela. Quelque chose comme:

### 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
la source