Comment faire pour que xargs gère les espaces et les caractères spéciaux de cat?

9

J'en ai un filequi contient une liste de noms. c'est à dire:

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

avec des espaces et quelques caractères spéciaux. Je voulais créer des répertoires à partir de ces noms, c'est-à-dire:

cat file | xargs -l1 mkdir

Il fait des répertoires individuels séparés par des espaces, par exemple Long, Name, One, Two, Three, au lieu de Long Name One (001), Long Name Two (201), Long Name Three (123).

Comment puis je faire ça?

Majal
la source

Réponses:

13

Utilisez -d '\n'avec votre xargscommande:

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

Depuis la page de manuel:

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

Exemple de sortie:

$ 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
la source
Vous avez besoin de GNU xargs pour l' -doption.
cuonglm
@cuonglm Je pense que j'ai principalement trouvé des xargs GNU. J'ai également vérifié 1 , 2 , 3 . oui BSD peut être le cas
Pandya
3

xargs attend un format d'entrée très spécial où les arguments sont délimités par des espaces ou des retours à la ligne (parfois d'autres formes d'espaces verticaux, parfois dépendants des paramètres régionaux actuels), et où des guillemets simples, des guillemets doubles et une barre oblique inversée peuvent être utilisés pour les échapper (mais dans un autre des citations shell).

-l1ne consiste pas à passer une ligne d'entrée en tant qu'argument unique à mkdir, mais à appeler une mkdirinvocation pour chaque ligne d'entrée mais avec des mots sur cette ligne toujours séparés en tant qu'arguments différents mkdir.

L'implémentation GNU a xargsajouté une -0option il y a des décennies pour accepter les entrées délimitées par NUL. C'est le moyen le plus évident de séparer les mots qui finiront par être des arguments d'une commande car le caractère NUL se trouve être le seul caractère qui ne peut pas apparaître dans un argument de commande ou un nom de fichier (le format de liste choisi qui place un fichier par ligne ne peut pas représenter tous les noms de fichiers possibles car il n'autorise pas la nouvelle ligne dans un nom de fichier).

Cela -0a été copié par plusieurs autres xargsimplémentations mais pas toutes.

Avec ceux que vous pouvez faire:

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

Cela appellera mkdirle moins de fois possible avec autant d'arguments que possible.

Mais notez que si fileest vide, mkdirsera toujours exécuté et vous obtiendrez une erreur de syntaxe en mkdirraison de l'argument manquant. GNU a xargsajouté une -roption pour celle qui a été copiée par quelques autres implémentations.

GNU a xargségalement ajouté (plus tard) une -doption pour pouvoir spécifier des délimiteurs arbitraires, mais je pense qu'aucune autre implémentation ne l'a copiée. Avec GNU xargs, le meilleur moyen est de:

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

En passant le fichier avec -a(également une extension GNU) au lieu de stdin, cela signifie que mkdirle stdin est préservé.

POSIX, vous devez post-traiter l'entrée pour la mettre au format attendu par xargs. Vous pouvez le faire par exemple avec:

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

Où nous enfermons chaque ligne entre guillemets doubles et s'échappons "comme "\""avant d'alimenter xargs.

Mais attention aux limitations possibles:

  • l'erreur lorsque le fichier est vide déjà mentionné ci-dessus
  • il peut échouer avec certaines implémentations (y compris de sed) si le contenu de filen'est pas du texte valide dans les paramètres régionaux actuels. Si filecontient des noms de fichier codant dans plusieurs jeux de caractères différents, ou un jeu de caractères différent de celui des paramètres régionaux, vous pouvez corriger les paramètres régionaux en C, ce qui devrait vous aider.
  • certaines xargsimplémentations ont des limites ridiculement basses sur la longueur maximale d'un argument (peut être aussi bas que 255 octets).

Pour contourner l' erreur de syntaxe en cas d'erreur d'entrée vide , vous pouvez écrire:

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

Rendez les noms nuls et séparez-les:

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

trremplacera le retour à la ligne qui sort catavec \0, et les -0drapeaux xargsdedans lui disent de diviser les arguments sur le \0.

Kira
la source
1

Vous pouvez le faire POSIXLEMENT avec l' -Ioption:

xargs -I % mkdir % < file

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

Steven Penny
la source
Bien que cela fonctionnerait avec l'exemple de l'OP, vous auriez toujours des problèmes avec les blancs de début, les guillemets simples, les guillemets doubles et les barres obliques inverses (et éventuellement les longues lignes et les séquences d'octets ne formant pas de caractères valides dans les paramètres régionaux).
Stéphane Chazelas