¿Cómo hacer que los xargs manejen espacios y caracteres especiales de cat?

9

Tengo un fileque contiene una lista de nombres. es decir:

Long Name One (001)
Long Name Two (201)
Long Name Three (123)
...

con espacios y algunos personajes especiales. Quería hacer directorios con estos nombres, es decir:

cat file | xargs -l1 mkdir

Se hace directorios individuales separados por espacios, es decir Long, Name, One, Two, Three, en lugar de Long Name One (001), Long Name Two (201), Long Name Three (123).

¿Cómo puedo hacer eso?

Majal
fuente

Respuestas:

13

Use -d '\n'con su xargscomando:

cat file | xargs -d '\n' -l1 mkdir

Desde la página de manual:

-d delim
              Input  items  are  terminated  by the specified character.  Quotes and backslash are not special; every
              character in the input is taken literally.  Disables the end-of-file string, which is treated like  any
              other  argument.   This can be used when the input consists of simply newline-separated items, although
              it is almost always better to design your program to use --null where this is possible.  The  specified
              delimiter  may be a single character, a C-style character escape such as \n, or an octal or hexadecimal
              escape code.  Octal and hexadecimal escape codes are understood as for the printf command.    Multibyte
              characters are not supported.

Salida de ejemplo:

$ ls
file

$ cat file
Long Name One (001)
Long Name Two (201)
Long Name Three (123)

$ cat file | xargs -d '\n' -l1 mkdir

$ ls -1
file
Long Name One (001)
Long Name Three (123)
Long Name Two (201)
Pandya
fuente
Necesita GNU xargs para la -dopción.
Cuonglm
@cuonglm Creo que principalmente encontré GNU xargs. También he comprobado 1 , 2 , 3 . sí BSD puede ser el caso
Pandya
3

xargs espera un formato de entrada muy especial donde los argumentos están delimitados por espacios en blanco o nuevas líneas (a veces otras formas de espacios en blanco verticales, a veces dependientes de la configuración regional actual), y donde se pueden usar comillas simples, comillas dobles y barras invertidas para escapar de ellas (pero en una forma diferente camino de las comillas).

-l1no es pasar una línea de entrada como un solo argumento mkdir, sino llamar a una mkdirinvocación para cada línea de entrada, pero con palabras en esa línea aún separadas como argumentos diferentes para mkdir.

La implementación de GNU xargsagregó una -0opción hace décadas para aceptar entradas delimitadas por NUL. Esa es la forma más obvia de separar palabras que terminarán siendo argumentos para un comando porque el carácter NUL resulta ser el único carácter que no puede aparecer en un argumento de comando o nombre de archivo (el formato de lista elegido que coloca un archivo por línea no puede representar todos los nombres de archivo posibles ya que no permite una nueva línea en un nombre de archivo).

Eso -0ha sido copiado por varias otras xargsimplementaciones, pero no todas.

Con los que puedes hacer:

<file tr '\n' '\0' | xargs -0 mkdir -p --

Eso requerirá la mkdirmenor cantidad de veces posible con tantos argumentos como sea posible.

Pero tenga en cuenta que si fileestá vacío, mkdirtodavía se ejecutará y obtendrá un error de sintaxis mkdirdebido al argumento que falta. GNU xargsagregó una -ropción para lo que ha sido copiado por algunas otras implementaciones.

GNU xargstambién agregó (más adelante) una -dopción para poder especificar delimitadores arbitrarios, pero no creo que ninguna otra implementación lo haya copiado. Con GNU xargs, la mejor manera es con:

xargs -rd '\n' -a file mkdir -p --

Al pasar el archivo con -a(también una extensión GNU) en lugar de stdin, eso significa que mkdirse preserva el stdin.

POSIXY, necesitaría postprocesar la entrada para ponerla en el formato esperado por xargs. Podrías hacerlo por ejemplo con:

<file sed 's/"/"\\""/g; s/^/"/; s/$/"/' | xargs mkdir -p --

Donde encerramos cada línea entre comillas dobles y escapamos de cada una "como "\""antes de alimentar a xargs.

Pero cuidado con las posibles limitaciones:

  • el error cuando el archivo está vacío ya mencionado anteriormente
  • puede fallar con algunas implementaciones (incluso de sed) si el contenido de fileno es texto válido en la configuración regional actual. Si filecontiene nombres de archivos que codifican en más de un conjunto de caracteres diferente, o un conjunto de caracteres diferente al de la configuración regional, puede corregir la configuración regional en C, lo que debería ayudar.
  • Algunas xargsimplementaciones tienen límites ridículamente bajos en la longitud máxima de un argumento (puede ser tan bajo como 255 bytes).

Para evitar el error de sintaxis en el error de entrada vacía , puede escribir:

<file sed 's/"/"\\""/g; s/^/"/; s/$/"/' |
  xargs sh -c '[ "$#" -eq 0 ] || exec mkdir -p -- "$@"' sh
Stéphane Chazelas
fuente
1

Haga los nombres nulos terminados y divididos allí:

cat file | tr '\n' '\0' | xargs -l1 -0 mkdir

trreemplazará la nueva línea con la que catsale \0, y las -0banderas en xargsle dicen que divida los argumentos en \0.

Kira
fuente
1

Puede hacer esto POSIXLY con la -Iopción:

xargs -I % mkdir % < file

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/xargs.html

Steven Penny
fuente
Si bien funcionaría con la muestra del OP, aún tendría problemas con espacios en blanco iniciales, comillas simples, comillas dobles y barras diagonales inversas (y posiblemente líneas largas y secuencias de bytes que no forman caracteres válidos en la configuración regional).
Stéphane Chazelas