Comment puis-je exécuter deux commandes sur une entrée, sans taper deux fois cette entrée?
Par exemple, la commande stat en dit long sur un fichier, mais n'indique pas son type de fichier:
stat fileName
La commande file indique le type d'un fichier:
file fileName
Vous pouvez effectuer cela en une seule ligne de cette façon:
stat fileName ; file fileName
Cependant, vous devez taper deux fois le nom de fichier.
Comment pouvez-vous exécuter les deux commandes sur la même entrée (sans taper l'entrée ou une variable de l'entrée deux fois)?
Sous Linux, je sais comment diriger les sorties, mais comment canaliser les entrées?
command-line
Lonniebiz
la source
la source
<
(entrée du fichier vers le côté gauche) ou|
(entrée du flux vers le côté droit). Il y a une différence.yank-last-arg
commande (raccourci par défautMeta-.
ouMeta-_
;Meta
étant souvent laAlt
touche gauche ) copiera le dernier paramètre de la ligne de commande précédente dans la ligne courante. Ainsi, après l'exécution,stat fileName
vous tapezfile [Meta-.]
et vous n'avez pas besoin de le taper àfileName
nouveau. J'utilise toujours ça.<
et>
sont redirection , pas la tuyauterie . Un pipeline connecte deux processus, tandis que la redirection réaffecte simplement stdin / stdout.Réponses:
Voici une autre façon:
Exemple:
Cela fonctionne dans
bash
etzsh
. Cela fonctionne également dansmksh
etdash
mais uniquement lorsqu'il est interactif. Dans AT&T ksh, cela ne fonctionne que lorsque lefile "$_"
est sur une ligne différente destat
celle.la source
!$
ci ne fonctionnera pas car se!$
développe jusqu'au dernier mot du dernier événement d'historique (donc de la ligne de commande entrée précédemment, pas de la commande stat).Je vais probablement faire raper mes phalanges pour cela, voici une combinaison hacky d'expansion bash brace et d'évaluation qui semble faire l'affaire
la source
csh
, nonbash
.bash
copié deksh
. Ce code fonctionnera dans tous les csh, tcsh, ksh, zsh, fish et bash.eval
? (se référant aux meilleurs commentaires)Avec
zsh
, vous pouvez utiliser des fonctions anonymes:Avec
es
lambdas:Vous pourriez également faire:
(faire le
stat
premier car lefile
mettra à jour le temps d'accès).Je ferais:
cependant, c'est à cela que servent les variables.
la source
{ stat -; file -;} < filename
est magique.stat
doit vérifier pour voir que son stdin est connecté à un fichier et signaler les attributs du fichier? Vraisemblablement, ne pas faire avancer le pointeur sur stdin, ce qui permetfile
de renifler le contenu de stdinstat -
ditstat
de faire unfstat()
sur son fd 0.{ $COMMAND_A -; $COMMAND_B; } < filename
variante ne fonctionnera pas dans le cas général si $ COMMAND_A lit réellement une entrée; par exemple{ cat -; cat -; } < filename
, imprime le contenu du nom de fichier une seule fois.stat -
est géré spécialement depuis coreutils-8.0 (octobre 2009), dans les versions antérieures, vous obtiendrez une erreur (sauf si vous avez un fichier appelé à-
partir d'une expérience précédente, auquel cas vous obtiendrez les mauvais résultats ...)stat -f0
place.Toutes ces réponses me semblent plus ou moins des scripts. Pour une solution qui n'implique vraiment aucun script et suppose que vous êtes assis au clavier en tapant dans un shell, vous pouvez essayer:
En bash, zsh et ksh au moins, en modes vi et emacs, en appuyant sur Echap, puis en soulignant insère le dernier mot de la ligne précédente dans le tampon d'édition de la ligne actuelle.
Ou, vous pouvez utiliser la substitution d'historique:
Dans bash, zsh, csh, tcsh et la plupart des autres shells,
!$
est remplacé par le dernier mot de la ligne de commande précédente lorsque la ligne de commande actuelle est analysée.la source
Esc _
? Pouvez-vous faire un lien avec la documentation officielle?La façon dont j'ai trouvé cela raisonnablement simple consiste à utiliser xargs, il prend un fichier / pipe et convertit son contenu en arguments de programme. Cela peut être combiné avec un tee qui divise un flux et l'envoie à deux programmes ou plus. Dans votre cas, vous avez besoin de:
Contrairement à beaucoup d'autres réponses, cela fonctionnera dans bash et la plupart des autres shells sous Linux. Je suggérerai que c'est un bon cas d'utilisation d'une variable mais si vous ne pouvez absolument pas en utiliser une, c'est la meilleure façon de le faire uniquement avec des tuyaux et des utilitaires simples (en supposant que vous n'avez pas de shell particulièrement sophistiqué).
De plus, vous pouvez le faire pour n'importe quel nombre de programmes en les ajoutant simplement de la même manière que ces deux après le tee.
MODIFIER
Comme suggéré dans les commentaires, il y a quelques défauts dans cette réponse, le premier est le potentiel d'entrelacement de sortie. Cela peut être résolu comme suit:
Cela forcera les commandes à s'exécuter à tour de rôle et la sortie ne sera jamais entrelacée.
Le deuxième problème est d'éviter la substitution de processus qui n'est pas disponible dans certains shells, ceci peut être réalisé en utilisant tpipe au lieu de tee comme suit:
Cela devrait être portable sur presque n'importe quel shell (j'espère) et résout les problèmes dans l'autre recommandation, cependant j'écris ce dernier de mémoire car mon système actuel n'a pas de tpipe disponible.
la source
ksh
(pas les variantes du domaine public),bash
etzsh
. Notez questat
etfile
s'exécutera simultanément, donc leur sortie sera peut-être entrelacée (je suppose que vous utilisez| cat
pour forcer la mise en mémoire tampon de sortie pour limiter cet effet?).Cette réponse est similaire à quelques autres:
Contrairement aux autres réponses, qui répètent automatiquement le dernier argument de la commande précédente, celle-ci vous oblige à compter:
Contrairement à certaines des autres réponses, cela ne nécessite pas de guillemets:
ou
la source
Ceci est similaire à quelques autres réponses, mais est plus portable que celui-ci et beaucoup plus simple que celui-ci :
ou
dans lequel
sh
est affecté à$0
. Bien qu'il y ait trois caractères de plus à taper, ce (deuxième) formulaire est préférable car c'est$0
le nom que vous donnez à ce script en ligne. Il est utilisé, par exemple, dans les messages d'erreur par le shell pour ce script, comme quandfile
oustat
ne peut pas être trouvé ou ne peut pas être exécuté pour une raison quelconque.Si vous voulez faire
pour un nombre arbitraire de fichiers, faites
qui fonctionne car
"$@"
est équivalent à"$1" "$2" "$3" …
.la source
$0
le nom que vous souhaitez donner à ce script en ligne. Il est utilisé par exemple dans les messages d'erreur par le shell pour ce script. Comme quandfile
oustat
ne peut pas être trouvé ou ne peut pas être exécuté pour une raison quelconque. Donc, vous voulez quelque chose de significatif ici. S'il n'est pas inspiré, utilisez-lesh
.Je pense que vous posez une mauvaise question, du moins pour ce que vous essayez de faire.
stat
et lesfile
commandes prennent des paramètres qui sont le nom d'un fichier et non son contenu. C'est plus tard la commande qui fait la lecture du fichier identifié par le nom que vous spécifiez.Un tuyau est censé avoir deux extrémités, l'une utilisée en entrée et l'autre en sortie, ce qui est logique car vous devrez peut-être effectuer un traitement différent en cours de route avec différents outils jusqu'à obtenir le résultat souhaité. Dire que vous savez comment diriger les sorties mais ne savez pas comment diriger les entrées n'est pas correct en principe.
Dans votre cas, vous n'avez pas du tout besoin d'un tube car vous devez fournir l'entrée sous la forme d'un nom de fichier. Et même si ces outils (
stat
etfile
) liraient stdin, le canal n'est plus pertinent ici car l'entrée ne devrait être modifiée par aucun des outils quand il arrivera au second.la source
Cela devrait fonctionner pour tous les noms de fichiers dans le répertoire en cours.
la source
}
... et ne commencent pas par-
. Certainesprintf
implémentations (zsh, bash, ksh93) ont un%q
. Aveczsh
, ça pourrait être:printf 'stat %q; file %1$q\n' ./* | zsh
sed
s///
je suppose, mais c'est à peu près de la portée - et si les gens mettent cela dans leurs noms de fichiers, ils devraient utiliser Windows.*
deux fois . Autant direstat -- *; file -- *
.*
est développée. L' extension de*
plus les deux commandes pour chaque argument dans*
est l'entrée. Voir?printf commands\ %s\;shift *
Il y a quelques références différentes à la «contribution» ici, donc je vais donner quelques scénarios en le comprenant d'abord. Pour votre réponse rapide à la question sous la forme la plus courte :
Ce qui précède effectuera une statistique sur le fichier de test, prendra (redirigera) son STDOUT et l'inclura dans la fonction spéciale suivante (la partie <()), puis affichera les résultats finaux de tout ce qui était, dans un nouveau fichier (fichier de sortie). Le fichier est appelé, puis référencé avec les fonctions intégrées bash (1 $ à chaque fois, jusqu'à ce que vous commenciez un nouvel ensemble d'instructions).
Votre question est excellente, et il existe plusieurs réponses et façons de le faire, mais elle change en effet avec ce que vous faites spécifiquement.
Par exemple, vous pouvez également boucler cela, ce qui est assez pratique. Un usage courant de ceci est, dans l'esprit de pseudo-code, est:
Prendre cela et développer ces connaissances vous permet de créer des choses telles que:
Cela exécutera un simple ls -A sur votre répertoire actuel, puis indiquera pendant que vous parcourrez chaque résultat du ls -A vers (et voici où c'est délicat!) Grep "thatword" dans chacun de ces résultats, et exécutez uniquement le précédent printf (en rouge) s'il a effectivement trouvé un fichier contenant "ce mot". Il enregistrera également les résultats de la grep dans un nouveau fichier texte, files_that_matched_thatword.
Exemple de sortie:
Tout cela a simplement imprimé le résultat ls -A, rien de spécial. Ajoutez-y quelque chose à grep cette fois:
Maintenant, relancez-le:
Bien que la réponse soit peut-être plus épuisante que ce que vous recherchez actuellement, je pense que conserver des notes pratiques comme celle-ci vous sera beaucoup plus utile dans les efforts futurs.
la source