Ajouter une nouvelle ligne dans un nom de fichier avec `mv`

11

C'est une question sérieuse. Je teste certains awkscripts et j'ai besoin de fichiers avec une nouvelle ligne dans leurs noms.


Est-il possible d'ajouter une nouvelle ligne dans un nom de fichier avec mv?

Maintenant, je peux le faire avec touch:

touch "foo
bar"

Avec le toucher, j'ai ajouté le caractère de nouvelle ligne par copier-coller. Mais je ne peux pas écrire fooReturnbardans ma coquille.

Comment renommer un fichier pour avoir une nouvelle ligne dans le nom de fichier?


Edit 2015/06/28; 19 h 08

Pour ajouter une nouvelle ligne dans zshJe peux utiliser, Alt+Return

UN B
la source
2
Pourquoi demandez-vous? Faites-vous une blague ou un tour à un ami?
Basile Starynkevitch
2
Ne supprimez pas la question, c'est utile. Mais évitez autant que possible les nouvelles lignes dans les noms de fichiers.
Basile Starynkevitch
2
Qu'est-ce qui vous fait penser que le faire mvserait différent de le faire avec touch? As-tu essayé la même chose?
Kevin
2
Eh bien, vous pouvez copier et coller dans la mvcommande aussi facilement que touch.
Kevin
2
Alors laissez-moi vous suggérer de formuler plus clairement votre OP: dans un environnement shell donné, je ne peux pas taper une nouvelle ligne dans une chaîne entre guillemets comme dans echo "a [return] b".
dan

Réponses:

22

C'est une mauvaise idée (d'avoir des caractères étranges dans les noms de fichiers) mais vous pourriez faire

 mv somefile.txt "foo
 bar"

(vous auriez aussi pu faire mv somefile.txt "$(printf "foo\nbar")"ou mv somefile.txt foo$'\n'bar, etc ... les détails sont spécifiques à votre shell. J'utilise zsh)

En savoir plus sur le globbing , par exemple glob (7) . Les détails peuvent être spécifiques au shell. Mais comprenez que cela /bin/mv est donné (par votre shell), via execve (2) , un tableau étendu d'arguments: l'expansion des arguments et la globalisation sont la responsabilité du shell appelant.

Et vous pouvez même coder un petit programme C pour faire de même:

#include <stdio.h>
#include <stdlib.h>
int main() {
  if (rename ("somefile.txt", "foo\nbar")) {
     perror("rename somefile.txt");
     exit(EXIT_FAILURE);
  };
  return 0;
}

Enregistrez le programme ci-dessus foo.c, compilez-le avec gcc -Wall foo.c -o foo puis exécutez./foo

De même, vous pouvez coder un script similaire en Perl, Ruby, Python, Ocaml, etc ....

Mais c'est une mauvaise idée. Évitez les sauts de ligne dans les noms de fichiers (cela déroutera l'utilisateur et pourrait casser de nombreux scripts).

En fait, je recommande même de n'utiliser que des lettres, des chiffres et des +-/._% caractères non accentués (en / étant le séparateur de répertoire) dans les chemins de fichiers. Les fichiers "cachés" (commençant par .) doivent être utilisés avec prudence et parcimonie. Je pense que l'utilisation de tout type d'espace dans un nom de fichier est une erreur. Utilisez un trait de soulignement à la place (par exemple foo/bar_bee1.txt) ou un moins (par exemple foo/bar-bee1.txt)

Basile Starynkevitch
la source
Oui, je sais que je peux copier et coller un caractère de nouvelle ligne, mais comment pourrais-je le faire sans copier-coller?
AB
4
Il n'y a pas de copier-coller ici. Il y a une nouvelle ligne tapée après foo.
dan
Cependant, cela fonctionne également par copier-coller
kos
1
C'est une mauvaise idée pour les fichiers habituels, mais un bon cas de test, comme l'OP l'a mentionné.
artdanil
5
@et ,serait plus inoffensif que -(comme en première position) ou %. Toutes ces recommandations visent principalement à contourner les bogues dans certaines applications (qui se cassent sur des noms de fichiers qui sont par ailleurs valides du point de vue du système d'exploitation). Il est rétrograde de demander aux gens de ne pas utiliser ces personnages plutôt que de corriger leurs bugs.
Stéphane Chazelas
19

Si vous utilisez bash, cette commande devrait fonctionner.

mv a $'b\nc'
favadi
la source
4
Fonctionne avec zshaussi =)
AB
3
bash, comme zsh et FreeBSD sh a copié cela depuis ksh93.
Stéphane Chazelas
Merci beaucoup! J'ai totalement oublié $ dans bash, alors que c'était ma propre réponse sur SO conseillant de l'utiliser - stackoverflow.com/questions/1825552/grep-a-tab-in-unix/…
antimirov
6

Je sais que vous avez demandé une mvsolution, cependant, malgré l'avertissement, cela peut être facilement fait avec rename(dans le paquet Perl):

~/tmp$ touch foo
~/tmp$ rename 's/$/\nbar/' foo
Unsuccessful stat on filename containing newline at /usr/share/perl5/File/Rename.pm line 69.
~/tmp$ ls
foo?bar
kos
la source
Merci pour rename, une très bonne idée. +1
AB
5

Un aspect de ce problème ne concerne pas vraiment awk- et seulement un peu sur le shell. Le problème est que sur un tty standard et canonique la plupart du temps, la discipline tty du noyau met en mémoire tampon vos entrées - en les faisant simplement écho sur votre écran et nulle part ailleurs - afin qu'il puisse gérer efficacement le retour en arrière et autres.

Cependant, lorsque vous appuyez sur Entrée ou entrez une nouvelle ligne, toutes ces données mises en mémoire tampon sont envoyées en même temps à l'application de lecture - généralement votre shell. Vous pouvez observer cela en surveillant $PS2après avoir entré un devis pendant. Lorsque le shell s'imprime, $PS2c'est parce qu'il vient de lire un bloc de votre entrée et n'est pas encore convaincu que vous avez terminé.

Donc, pour plus de commodité, ce dont vous avez besoin est un moyen d'envoyer une ligne \nélectronique dans le tampon du terminal sans avoir à pousser immédiatement toutes ces autres entrées. La façon standard de le faire est avec la séquence de touches CTRL+V- qui cite pour le terminal votre prochain caractère d'entrée. Faites CTRL+Vensuite CTRL+J- parce que ce dernier est généralement la façon de taper une ligne \nélectronique littérale . Vous saurez que cela fonctionne lorsque vous ne voyez pas, $PS2car le shell n'a toujours pas lu votre entrée.

Notez bien que quand il ne lire que votre entrée plus tôt CTRL+Vaura fait aucune différence pour la coquille du tout - qu'il ne cite que pour la ligne-discipline. Vous voudrez certainement citer la nouvelle ligne pour citer quelque chose de significatif.

Soit dit en passant, CTRL+Vpeut être utilement appliqué par d'autres moyens - par exemple, ce "$(printf \\33)"n'est pas le seul moyen d'écrire un ESCcaractère dans un script shell - et ce n'est même pas le plus simple. Vous pouvez littéralement entrer n'importe quel caractère que votre clavier enverra sans que le pilote d'entrée ne tente de l'interpréter si vous y échappez tout d'abord de cette manière.

J'aime souvent utiliser des <tab> s sur la ligne de commande sans que le shell essaie de terminer quoi que ce soit. Parce que les shells qui effectuent la complétion configurent généralement <tab> de manière synonyme de stty eol \t, pour que leurs systèmes de complétion fonctionnent, cela CTRL+Vfonctionne pour moi même dans des environnements inconnus.

mikeserv
la source
2
Merci d'avoir mentionné 'Ctrl + V'. J'étais sur le point de poster une réponse à ce sujet et j'ai ensuite vu votre explication.
artdanil