web-dev-qa-db-fra.com

bash: le plus court chemin pour obtenir la n-ième colonne de sortie

Supposons que, au cours de votre journée de travail, vous rencontriez de manière répétée la forme de sortie en colonnes suivante d'une commande de bash (dans mon cas, en exécutant svn st dans mon répertoire de travail Rails):

?       changes.patch
M       app/models/superman.rb
A       app/models/superwoman.rb

afin de travailler avec le résultat de votre commande - dans ce cas les noms de fichiers - une sorte d'analyse est nécessaire pour que la deuxième colonne puisse être utilisée comme entrée pour la commande suivante.

Ce que j’ai fait est d’utiliser awk pour accéder à la deuxième colonne, par exemple. quand je veux supprimer tous les fichiers (pas que ce soit une cas typique d'utilisation :), je ferais:

svn st | awk '{print $2}' | xargs rm

Comme je tape souvent cela, la question qui se pose est naturelle: existe-t-il un moyen plus court (donc plus froid) d’accomplir cela en bash?

REMARQUE: Ce que je demande est essentiellement une question de commande Shell même si mon exemple concret se trouve sur mon flux de travail svn. Si vous pensez que le flux de travail est idiot et suggérez une approche alternative, je ne vous rejeterai probablement pas, mais d'autres pourraient le faire, car la question ici est vraiment de savoir comment obtenir la sortie de la n-ième colonne en bash, le plus rapidement possible. . Merci :)

140
Sv1

Vous pouvez utiliser cut pour accéder au deuxième champ:

cut -f2

Edit: Désolé, je ne savais pas que SVN n'utilisait pas d'onglets dans sa sortie, donc c'est un peu inutile. Vous pouvez adapter cut à la sortie, mais c'est un peu fragile - quelque chose comme cut -c 10- fonctionnerait, mais la valeur exacte dépendra de votre configuration.

Une autre option est quelque chose comme: sed 's/.\s\+//'

99
porges

Pour accomplir la même chose que:

svn st | awk '{print $2}' | xargs rm

en utilisant uniquement bash, vous pouvez utiliser:

svn st | while read a b; do rm "$b"; done

Certes, ce n’est pas plus court, mais c’est un peu plus efficace et il gère correctement les espaces dans les noms de fichiers.

89
Rick Sladkey

Je me suis retrouvé dans la même situation et j'ai fini par ajouter ces alias à mon fichier .profile:

alias c1="awk '{print \$1}'"
alias c2="awk '{print \$2}'"
alias c3="awk '{print \$3}'"
alias c4="awk '{print \$4}'"
alias c5="awk '{print \$5}'"
alias c6="awk '{print \$6}'"
alias c7="awk '{print \$7}'"
alias c8="awk '{print \$8}'"
alias c9="awk '{print \$9}'"

Ce qui me permet d'écrire des choses comme celle-ci:

svn st | c2 | xargs rm
28
StackedCrooked

Essayez le zsh. Il supporte le suffixe alias, vous pouvez donc définir X dans votre .zshrc comme étant

alias -g X="| cut -d' ' -f2"

alors vous pouvez faire:

cat file X

Vous pouvez aller plus loin et le définir pour la nième colonne:

alias -g X2="| cut -d' ' -f2"
alias -g X1="| cut -d' ' -f1"
alias -g X3="| cut -d' ' -f3"

qui affichera la nième colonne du fichier "fichier". Vous pouvez le faire pour une sortie grep ou une sortie inférieure. Ceci est très pratique et une caractéristique tueur du zsh.

Vous pouvez aller plus loin et définir D comme étant:

alias -g D="|xargs rm"

Maintenant vous pouvez taper:

cat file X1 D

supprimer tous les fichiers mentionnés dans la première colonne du fichier "fichier".

Si vous connaissez le bash, le zsh n’est pas vraiment un changement, à part quelques nouvelles fonctionnalités.

HTH Chris

15
Chris

Comme vous semblez ne pas connaître les scripts, voici un exemple.

#!/bin/sh
# usage: svn st | x 2 | xargs rm
col=$1
shift
awk -v col="$col" '{print $col}' "${@--}"

Si vous enregistrez ceci dans ~/bin/x et que vous vous assurez que ~/bin est dans votre PATH (maintenant, vous pouvez et devez mettre dans votre .bashrc), vous disposez de la commande la plus courte possible. pour extraire généralement la colonne n; x n.

Le script doit effectuer la vérification d'erreur et le traitement corrects s'il est appelé avec un argument non numérique ou avec un nombre incorrect d'arguments, etc. mais développer cette version essentielle nue sera dans l'unité 102.

Peut-être voudrez-vous étendre le script pour autoriser un délimiteur de colonne différent. Awk par défaut analyse les entrées dans les champs sur les espaces; pour utiliser un autre délimiteur, utilisez -F ':': est le nouveau délimiteur. L'implémenter comme une option dans le script le rend légèrement plus long, alors je laisse cela comme exercice pour le lecteur.


Usage

Étant donné un fichier file:

1 2 3
4 5 6

Vous pouvez soit le transmettre via stdin (en utilisant un inutile cat simplement en tant qu'espace réservé pour quelque chose de plus utile);

$ cat file | sh script.sh 2
2
5

Ou fournissez-le comme argument au script:

$ sh script.sh 2 file
2
5

Ici, sh script.sh suppose que le script est enregistré sous le nom script.sh dans le répertoire en cours; Si vous enregistrez-le avec un nom plus utile quelque part dans votre PATH et le marquez comme exécutable, comme dans les instructions ci-dessus, utilisez évidemment le nom utile à la place (et non sh).

8
tripleee

Il semble que vous ayez déjà une solution. Pour faciliter les choses, pourquoi ne pas simplement mettre votre commande dans un script bash (avec un nom court) et simplement l'exécuter au lieu de taper cette commande 'longue' à chaque fois?

5
Tarek Fadel

Si vous êtes d'accord avec la sélection manuelle de la colonne, vous pourriez être très rapide en utilisant pick :

svn st | pick | xargs rm

Allez dans n'importe quelle cellule de la 2e colonne, appuyez sur c puis sur enter.

2
Bernardo Rufino

Notez que ce chemin de fichier ne doit pas obligatoirement figurer dans la deuxième colonne de la sortie svn st. Par exemple, si vous modifiez le fichier et modifiez sa propriété, ce sera la 3ème colonne.

Voir les exemples de sorties possibles en:

svn help st

Exemple de sortie:

 M     wc/bar.c
A  +   wc/qax.c

Je suggère de couper les 8 premiers caractères de:

svn st | cut -c8- | while read FILE; do echo whatever with "$FILE"; done

Si vous voulez être sûr à 100% et gérer des noms de fichiers sophistiqués avec des espaces à la fin, par exemple, vous devez analyser la sortie xml:

svn st --xml | grep -o 'path=".*"' | sed 's/^path="//; s/"$//'

Bien sûr, vous voudrez peut-être utiliser un analyseur XML réel au lieu de grep/sed.

1
Michał Šrajer