Utilisation de sed get substring between two double quotes


14

J'ai un dossier

xyz... rsync: "/home/path/to/file": Permission denied (13) rsync:
"/home/path/to/file1": Permission denied (13) rsync:
"/home/path/to/file2": Permission denied (13) rsync:
"/home/path/to/file3": Permission denied (13)

Maintenant, je veux extraire les chemins de fichiers uniquement et les stocker dans un autre fichier. Le fichier de sortie est comme:

/home/path/to/file 
/home/path/to/file1 
/home/path/to/file2
/home/path/to/file3

En utilisant sed ou awk, comment faire?

J'ai essayé sed -n '/"/,/"/p' myfilemais ça ne marche pas.


3
À ceux qui votent pour clore - Comment cela peut-il être hors sujet? Il s'agit de programmation shell !! C'est la PROGRAMMATION qui est SUR LE SUJET de Stack Overflow!
Jonathan Leffler

2
Bienvenue dans Stack Overflow. Comme vous pouvez le voir, nous avons parfois des problèmes avec des personnes ayant des doigts gâchettes qui déclenchent la fermeture de bonnes questions (comme celle-ci) avec de mauvaises raisons de fermeture. Cela n'arrive pas très souvent (ou je n'arrive pas à voir le problème à temps si souvent), mais cela arrive. N'oubliez pas de lire la FAQ avant trop longtemps.
Jonathan Leffler

Réponses:


17

Vous pouvez diriger stderr de votre commande rsync vers un script awk:

awk -F '"' '{print $2}' 

Ou à une commande de coupe comme celle-ci:

cut -d'"' -f2

2
Ou, plus court:cut -d\" -f2

@AndersJohansson: Merci d'avoir ajouté votre commande cut pour répondre également.
anubhava

Je pense que cela ne fonctionnera pas .. comme vous pouvez voir le numéro de champ du chemin de fichier n'est pas fixe $ 2 ou f2 ..Merci!

En fait, rsync écrira toujours le chemin de fichier d'abord entre "et "sur stderr.
anubhava

1
@ Jam88: En fait, cela fonctionnera en raison de la façon dont anubbhava l'a écrit. Le délimiteur de champ est défini sur un guillemet double. Cela signifie que tout jusqu'au premier guillemet double (éventuellement une chaîne vide) est $1; tout entre les premier et deuxième guillemets doubles est $2; et tout après la deuxième citation double est en $3( $4, ...). Le nom du fichier est (apparemment) toujours entre les deux premiers guillemets doubles, donc cette solution devrait fonctionner (et l'a fait quand je l'ai testée).
Jonathan Leffler

6

En utilisant sed:

sed 's/^[^"]*"\([^"]*\)".*/\1/'

Cela recherche: début de ligne, une série de non-guillemets, un guillemet double, capture une série de non-guillemets, un guillemet double et tout autre élément sur la ligne, et le remplace par le matériau capturé.

$ sed 's/^[^"]*"\([^"]*\)".*/\1/' <<'EOF'
> xyz... rsync: "/home/path/to/file": Permission denied (13) rsync:
> "/home/path/to/file1": Permission denied (13) rsync:
> "/home/path/to/file2": Permission denied (13) rsync:
> "/home/path/to/file3": Permission denied (13)
> EOF
/home/path/to/file
/home/path/to/file1
/home/path/to/file2
/home/path/to/file3
$

Testez sur RHEL 5 Linux avec GNU sed, mais uniquement en utilisant des fonctionnalités qui auraient fonctionné dans la version UNIX ™ de la 7e édition sed.

Soit dit en passant, une façon légèrement plus simple de le faire est d'utiliser deux commandes de substitution; changer tout jusqu'à et y compris le premier guillemet double en une chaîne vide (c'est une séquence de zéro ou plusieurs non guillemets suivie d'un guillemet double); changer tout après ce qui est maintenant la première citation double à rien:

sed 's/^[^"]*"//; s/".*//'

Par ailleurs, la commande que vous avez essayée (`sed -n '/" /, / "/ p') imprime d'une ligne contenant un guillemet double à la ligne suivante contenant un guillemet double, sans modifier les lignes du tout. C'est pourquoi cela n'a pas semblé fonctionner pour vous - il a fait ce que vous avez demandé, mais ce que vous lui avez demandé de faire n'était pas ce que vous aviez l'intention de lui demander de faire.

En termes d'efficacité, il est peu probable qu'il y ait une différence mesurable dans les performances. En termes de facilité d'entretien, je soupçonne que ce dernier est moins pénalisant pour les cellules cérébrales.


1

Si votre version de grepprend en charge Perl-regexp:

grep -oP '(?<=")/home/.*?(?=")' file >> anotherfile

Résultats:

/home/path/to/file
/home/path/to/file1
/home/path/to/file2
/home/path/to/file3

Vous pouvez également rendre cela moins strict, pour faire correspondre n'importe quoi entre les doubles si vous le souhaitez:

grep -oP '(?<=")[^"]*' file >> anotherfile

Avez-vous besoin de rendre le .*non-gourmand .*?au cas où il y aurait un double guillemet supplémentaire plus tard dans la ligne? Ou utiliser [^"]*à la place de .*?
Jonathan Leffler

-1

Utilisez l'opérateur >> pour enregistrer toute sortie dans un fichier.

Comme

grep -r "pattern" * >> file.txt

Il suffit donc de changer cela pour votre scénario spécifique en utilisant sed en ajoutant

>> filename

à la commande


Le grep -reffectue une recherche récursive dans tous les répertoires répertoriés dans les arguments ( *). Le modèle que vous avez en tête n'est pas clair, mais grepil reprendra toute la ligne. Le but de l'exercice est de collecter des informations à partir d'une partie d'une ligne. Si vous utilisez GNU grep, il existe des moyens de le faire ( -o); elles ne sont pas standard (sauf dans la mesure où GNU définit une norme de facto). De même avec l'utilisation d'expressions régulières PCRE; ce sont une autre extension GNU. Ils sont parfaits si vous avez GNU grepet n'avez pas l'intention de travailler sur des plates-formes où GNU grepn'est pas disponible par défaut.
Jonathan Leffler

Désolé d'avoir raté ça, je pensais qu'il voulait savoir en général ce qu'il fallait faire pour mettre la sortie dans un fichier, et grep n'était qu'un exemple.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.