Question
Disons que je viens de saisir cette commande pour obtenir le nombre de lignes contenant une chaîne particulière:
me@machine $ command-producing-multi-line-output | grep -i "needle" | wc -l
Maintenant, comment pourrais-je remplacer rapidement "needle"
avec un autre mot?
Solution inefficace actuelle
En ce moment je:
Up
sur le clavier pour charger la dernière commande.Left
ou Ctrl
+ Left
jusqu'à ce que j'atteigne "needle"
.Backspace
ou Ctrl
+ w
pour supprimer le mot.Enter
.Cela semble assez inefficace.
Tentatives de solutions non fonctionnelles
J'ai essayé de rechercher des raccourcis historiques comme !!:sg/needle/new-needle
; mais cela pose quelques problèmes:
Up
pour mettre !!:sg/needle/new-needle
de retour sur la ligne de commande. Faire cela montrera simplement à quoi il se développe (en revenant directement au problème d'origine).needle
et new-needle
(c'est à dire. !!:sg/new-needle-from-before/yet-another-new-needle
).needle
au lieu d'utiliser quelque chose comme !:4
ou $4
(c'est à dire. !!:sg/!:4/new-needle
ou !!:sg/$4/new-needle
) pour gagner du temps et des frappes, quelle que soit la longueur de l'aiguille.J'ai aussi trouvé des choses comme !:^-3 "new-needle" !:5-$
mais qui a aussi des problèmes:
Je crois qu'il doit y avoir un moyen super rapide de faire ce que je veux faire, et que certains gourous de Linux le savent. Je serais très reconnaissant pour toute contribution ou suggestion à ce sujet.
ÉDITER:
Contexte
Juste un peu de contexte, je travaille beaucoup avec OpenStack sur la ligne de commande et je me retrouve souvent devant remplacer un paramètre à plusieurs endroits dans une longue chaîne de commandes de commandes piped.
La façon dont nos environnements OpenStack sont configurés, plusieurs ingénieurs partagent un seul utilisateur stack
sur un nombre illimité de serveurs, et il existe plusieurs clusters dans plusieurs environnements. Donc, une fonction déclarée dans .bashrc
ou .profile
le fichier n'est pas vraiment possible.
J'espère quelque chose de portable qui peut être utilisé rapidement avec une configuration nulle ou très minimale requise. Sinon, il se peut que je doive simplement recourir à une solution entièrement extérieure au shell (comme le remplacement du presse-papiers dans AutoHotKey/Keyboard Maestro/AutoKey, etc.).
J'ai rencontré une situation similaire et présente ma solution ici au cas où ce serait un modèle utile.
Une fois que je me rends compte que je modifie à plusieurs reprises une donnée ennuyeuse à remplacer de manière interactive, je m'arrête et j'écris une petite boucle while
:
$ command-producing-multi-line-output | grep -i "needle" | wc -l
0
$ command-producing-multi-line-output | grep -i "haystack" | wc -l
0
$ while read needle; do
Up-Arrow à la précédente command-producing-multi-line-output
ligne et remplacer "haystack"
avec "$needle"
command-producing-multi-line-output | grep -i "$needle" | wc -l; done
something
0
something else
0
gold
1
(se terminant par Control+D)
Ou la variation légèrement plus fantaisiste:
$ while read needle; do
printf 'For: %s\n' "$needle"
command-producing-multi-line-output | grep -i "$needle" | wc -l; done
Si je vois un avenir pour cette ligne de commande au-delà de "aujourd'hui", je l'écrirai dans une fonction ou un script.
C'est exactement à quoi sert expansion historique :
$ command-producing-multi-line-output | grep -i "needle" | wc -l
...
$ ^needle^nipple
command-producing-multi-line-output | grep -i "nipple" | wc -l
...
Un alias utile dans bash
est
alias r='fc -s'
La commande r
se trouve souvent dans d'autres shells par défaut1et répète la commande la plus récente de l'histoire. Le manuel bash
y fait même référence comme un alias utile pour définir:
[...] A useful alias to use with this is ``r="fc -s"'',
so that typing ``r cc'' runs the last command beginning with
``cc'' and typing ``r'' re-executes the last command.
Il vous permettra également d'effectuer des remplacements dans le texte de la dernière commande.
Ici, j'exécute votre commande, puis j'utilise l'alias ci-dessus pour remplacer le mot needle
par le mot haystack
:
$ command-producing-multi-line-output | grep -i "needle" | wc -l
bash: command-producing-multi-line-output: command not found
0
$ r needle=haystack
command-producing-multi-line-output | grep -i "haystack" | wc -l
bash: command-producing-multi-line-output: command not found
0
Lorsque j'utilise r needle=haystack
sur la ligne de commande, le Shell imprime la commande qu'il va exécuter puis l'exécute immédiatement. Comme vous pouvez le voir, il remplace également la Parole.
Les erreurs sont évidemment dues au fait que je n'ai pas de command-producing-multi-line-output
, mais ce n'est pas important dans cet exercice.
La commande fc
ne sera pas enregistrée dans votre historique, mais vous pouvez la faire enregistrer en la créant comme une fonction Shell comme ceci:
fc() {
command fc "$@"
history -s fc "$@" # append the given fc command to history
}
Vous pouvez alors faire
$ command-producing-multi-line-output | grep -i "needle" | wc -l
bash: command-producing-multi-line-output: command not found
0
Réexécutez la commande la plus récente commençant par comm
et remplacez needle
à l'aide de r needle=haystack comm
:
$ r needle=haystack comm
command-producing-multi-line-output | grep -i "haystack" | wc -l
bash: command-producing-multi-line-output: command not found
0
Réexécutez la commande la plus récente, mais remplacez haystack
, à l'aide de r haystack=beeswax
:
$ r haystack=beeswax
fc -s needle=beeswax comm
command-producing-multi-line-output | grep -i "beeswax" | wc -l
bash: command-producing-multi-line-output: command not found
0
Notez le double appel qui est effectué vers fc
dans cette dernière ligne, d'abord via notre alias r
puis via notre fonction fc
.
De toute évidence, l'enregistrement de la commande fc
dans l'historique vous fait courir le risque d'appeler accidentellement fc -s
récursivement.
1Dans zsh
c'est une commande intégrée, dans OpenBSD ksh
c'est un alias par défaut pour fc -s
, dans ksh93
c'est un alias par défaut pour hist -s
, etc.
L'OP indique qu'une fonction déclarée dans .bashrc
n'est pas pratique, mais rien n'empêche d'en déclarer une de manière interactive. Comme il s'agit uniquement de la session en cours, un nom de lettre unique convient, par ex. q
.
Commençant par
$ command-producing-multi-line-output | grep -i "haystack" | wc -l
presse Up;} pour terminer une fonction, modifiez le haystack
en $1
, Control+aq(){Space obtenir
q(){ command-producing-multi-line-output | grep -i "$1" | wc -l;}
alors vous pouvez utiliser q needle
, q haystack
, q something
. Je préfère cela à la solution while
dans la réponse actuellement acceptée car elle permet d'autres commandes entre la recherche/le comptage.
Pour ce cas particulier, je définirais en fait
q(){ command-producing-multi-line-output | grep -i "$@";}
comme je voudrais probablement voir les lignes qui correspondent réellement à un moment donné, donc je pourrais utiliser q needle | wc -l
ou mieux q -c needle
pour compter et q needle
pour voir les lignes.
Comme la définition de la fonction va être stockée dans l'historique bash, il est généralement possible de recharger la définition si elle est nécessaire dans une prochaine session.
Sommaire: Ctrl+R nee
Alt+D haystack
Enter
Presse Ctrl+R pour démarrer une inversion recherche incrémentale et tapez quelques caractères du texte que vous souhaitez remplacer (ou du texte très proche) jusqu'à ce que la partie souhaitée de la ligne de commande souhaitée apparaisse (elle n'a pas être la ligne de commande précédente). presse Ctrl+R pour rechercher l'occurrence précédente. presse Ctrl+S d'aller de l'avant si vous êtes allé trop loin. presse Backspace si vous avez mal saisi un personnage.
Une fois que vous avez atteint l'endroit souhaité, vous pouvez utiliser des touches comme Delete, Alt+D ou Alt+Backspace pour commencer à supprimer du texte autour du curseur, ou tout simplement Escape pour quitter la recherche incrémentielle au point que vous avez atteint.
Presse Enter uniquement lorsque vous avez terminé de modifier la ligne de commande. Notez que Enter lors d'une recherche incrémentielle a sa signification normale pour exécuter la ligne de commande actuelle telle qu'elle est: elle ne se contente pas de quitter le mode de recherche (appuyez sur Escape pour simplement quitter le mode de recherche).
Dans ces situations, j'utilise souvent une variable d'environnement pour le paramètre comme suit:
ARG1=needle; command-producing-multi-line-output | grep -i "${ARG1}" | wc -l
La première affectation définit une variable d'environnement qui peut ensuite être utilisée dans les commandes suivantes. Cela sert de paramètre qui peut être modifié selon les besoins. Notez que cela définira une variable d'environnement pour la durée de vie de ce shell, mais j'ai trouvé que pour les commandes rapides et spécifiques, c'est correct. Les règles habituelles s'appliquent à l'expansion des variables, comme les guillemets simples '$ {ARG1}' ne développeront pas la variable. Des informations générales peuvent être trouvées à ce sujet (en voici une) https://www.gnu.org/software/bash/manual/html_node/Quoting.html
Je me retrouve à utiliser fzf
pour cela.
Si vous êtes prêt à installer quelque chose comme fzf
et que votre sortie est normalement inférieure à un écran, essayez ceci:
echo | fzf -q 'shuf < /usr/share/dict/words | grep needle | head' --preview-window=up:99% --preview="eval {q}"
Une fois la sortie affichée, placez le curseur à la fin du mot "aiguille", appuyez sur Ctrl-W et tapez botte de foin. Ensuite, appuyez à nouveau sur Ctrl-W et tapez un autre mot. Etc.
Cependant, je ne conseillerais pas cela pour les commandes qui changent le système :-) Vous utilisez la fonction d'aperçu de fzf
pour exécuter la commande, et fzf essaie de l'exécuter dès que quelque chose change. Par exemple, lorsque vous appuyez sur Ctrl-W, vous verrez grep montrant son message d'utilisation car momentanément, jusqu'à ce que vous tapiez le premier caractère du mot de remplacement, il n'y a aucun argument.
J'utilise cela si souvent que j'ai un script appelé try
, qui est fondamentalement le suivant:
#!/bin/bash
export FZF_DEFAULT_COMMAND=echo
fzf -q "$*" --preview-window=up:99% --preview="eval {q}"
Ouvrez un éditeur approprié qui est construit pour ce genre de choses. Par défaut, vous pouvez le faire avec Ctrl-xCtrl-e, cependant, si vous avez activé le mode d'édition vi (une façon est set -o vi
), vous pouvez aussi utiliser Escv.
Une fois sur place, utilisez la commande de substitution de votre éditeur. Dans vi (m), par exemple,
:%substitute/needle/haystack/g