Usando sed obtener subcadena entre dos comillas dobles

14

Tengo un archivo

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)

Ahora quiero extraer solo las rutas de archivo y almacenarlo en otro archivo. El archivo de salida es como:

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

Usando sed o awk, ¿cómo puedo hacer esto?

Lo he intentado sed -n '/"/,/"/p' myfilepero no funciona.

XemX
fuente
3
Para aquellos que votan para cerrar: ¿cómo puede ser posible que esto esté fuera de tema? ¡Se trata de la programación de shell! ¡Eso es PROGRAMACIÓN que está EN EL TEMA para Stack Overflow!
Jonathan Leffler
2
Bienvenido a Stack Overflow. Como puede ver, ocasionalmente tenemos problemas con personas con picazón en los dedos del gatillo que cierran preguntas perfectamente buenas (como esta) con malas razones para el cierre. No sucede con tanta frecuencia (o no veo el problema a tiempo con tanta frecuencia), pero sucede. No olvides leer las preguntas frecuentes antes de mucho tiempo.
Jonathan Leffler

Respuestas:

17

Puede canalizar stderr de su comando rsync a un script awk:

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

O para un comando de corte como este:

cut -d'"' -f2
anubhava
fuente
2
O, más corto:cut -d\" -f2
@AndersJohansson: Gracias también agregué tu comando de corte para responder.
anubhava
Creo que esto no va a funcionar ... como puede ver, el número de campo de la ruta del archivo no está fijado en $ 2 o f2 ... ¡Gracias!
En realidad, rsync siempre escribirá filepath primero entre "y "en stderr.
anubhava
1
@ Jam88: En realidad, funcionará debido a la forma en que anubbhava lo ha escrito. El delimitador de campo se establece en comillas dobles. Eso significa que todo hasta la primera comilla doble (posiblemente una cadena vacía) es $1; todo entre la primera y la segunda comillas dobles es $2; y todo después de la segunda comilla doble está en $3( $4, ...). El nombre del archivo está (aparentemente) siempre entre las dos primeras comillas dobles, por lo que esta solución debería funcionar (y lo hice cuando lo probé).
Jonathan Leffler
6

Utilizando sed:

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

Eso busca: comienzo de línea, una serie de no comillas, una comilla doble, captura una serie de no comillas, una comilla doble y cualquier otra cosa en la línea, y lo reemplaza por el material capturado.

$ 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
$

Probar en RHEL 5 Linux con GNU sed, pero solo utilizando características que habrían funcionado en la 7ª edición de la versión UNIX ™ de sed.

Por cierto, una forma un poco más simple de hacerlo es con dos comandos sustitutos; cambie todo hasta e incluyendo la primera comilla doble a una cadena vacía (es una secuencia de cero o más comillas no seguidas de una comilla doble); cambie todo después de lo que ahora es la primera cita doble a nada:

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

Por cierto, el comando que intentó ('sed -n' / "/, /" / p ') se imprime desde una línea que contiene una comilla doble a la siguiente línea que contiene una comilla doble, sin editar las líneas en absoluto. Por eso no pareció funcionar para usted: hizo lo que le pidió, pero lo que le pidió que hiciera no era lo que tenía la intención de pedirle.

En cuanto a la eficiencia, es poco probable que haya una diferencia apreciable en el rendimiento. En términos de facilidad de mantenimiento, sospecho que este último es menos exigente para las células cerebrales.

Jonathan Leffler
fuente
1

Si su versión de grepadmite Perl-regexp:

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

Resultados:

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

También puede hacer que esto sea menos estricto, para que coincida con cualquier cosa entre los dobles si lo desea:

grep -oP '(?<=")[^"]*' file >> anotherfile
Steve
fuente
¿Necesita hacer lo .*no codicioso por .*?si acaso hay una cita doble adicional más adelante en la línea? O utilizar [^"]*en lugar de .*?
Jonathan Leffler
-1

Use el operador >> para guardar cualquier salida en un archivo.

Me gusta

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

Así que solo cambie eso para su escenario específico usando sed agregando

>> filename

a la orden

AStupidNoob
fuente
El grep -rhace una búsqueda recursiva utilizando otros directorios que aparecen en los argumentos ( *). No está claro qué patrón tienes en mente, pero greprecogerá toda la línea. El propósito del ejercicio es recopilar información de parte de una línea. Si está utilizando GNU grep, hay formas de hacerlo ( -o); estos no son estándar (excepto en la medida en que GNU define un estándar de facto). De manera similar con el uso de expresiones regulares PCRE; esas son otra extensión de GNU. Están bien si tiene GNU grepy no planea trabajar en plataformas donde GNU grepno está disponible por defecto.
Jonathan Leffler
Lo siento, me perdí eso, pensé que quería saber en general qué hacer para poner la salida en un archivo, y grep fue solo un ejemplo.