Fügen Sie einem Dateinamen mit `mv` eine neue Zeile hinzu

10

Das ist eine ernste Frage. Ich awkteste einige Skripte und benötige Dateien mit einem Zeilenumbruch im Namen.


Ist es möglich, mit einem Dateinamen eine neue Zeile hinzuzufügen mv?

Ich jetzt kann ich das tun mit touch:

touch "foo
bar"

Mit Berührung habe ich das Zeilenumbruchzeichen pro Kopieren und Einfügen hinzugefügt. Aber ich kann nicht fooReturnbarin meine Shell schreiben .

Wie kann ich eine Datei umbenennen, um eine neue Zeile im Dateinamen zu haben?


Bearbeiten 28.06.2015; 19:08 Uhr

Um eine neue Zeile hinzuzufügen, zshkann ich Alt+ verwendenReturn

AB
quelle
2
Warum fragst du? Machst du einem Freund einen Witz oder Trick?
Basile Starynkevitch
2
Entfernen Sie die Frage nicht, es ist nützlich. Vermeiden Sie jedoch neue Zeilen in Dateinamen so weit wie möglich.
Basile Starynkevitch
2
Was lässt Sie denken, dass es mvanders wäre, es zu tun, als es zu tun touch? Hast du das Gleiche versucht?
Kevin
2
Nun, Sie können mvgenauso einfach kopieren und in den Befehl einfügen wie touch.
Kevin
2
Lassen Sie mich Ihnen daher vorschlagen, Ihr OP klarer zu formulieren: In einer bestimmten Shell-Umgebung kann ich keine neue Zeile in eine Zeichenfolge in Anführungszeichen eingeben, wie im Echo "a [return] b".
Dan

Antworten:

21

Es ist eine schlechte Idee (seltsame Zeichen in Dateinamen zu haben), aber Sie könnten es tun

 mv somefile.txt "foo
 bar"

(Sie hätten es auch tun können mv somefile.txt "$(printf "foo\nbar")"oder mv somefile.txt foo$'\n'barusw. Details sind spezifisch für Ihre Shell. Ich verwende zsh)

Lesen Sie mehr über Globbing , zB Glob (7) . Details können Shell-spezifisch sein. Verstehen Sie jedoch, dass /bin/mv (von Ihrer Shell) über execve (2) ein erweitertes Array von Argumenten angegeben wird: Die Erweiterung und das Globbing von Argumenten liegen in der Verantwortung der aufrufenden Shell.

Und Sie könnten sogar ein winziges C-Programm codieren, um dasselbe zu tun:

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

Speichern Sie das obige Programm in foo.c, kompilieren Sie es mit gcc -Wall foo.c -o foo und führen Sie es aus./foo

Ebenso können Sie ein ähnliches Skript in Perl, Ruby, Python, Ocaml usw. codieren.

Das ist aber eine schlechte Idee. Vermeiden Sie Zeilenumbrüche in Dateinamen (dies verwirrt den Benutzer und kann viele Skripte beschädigen ).

Eigentlich empfehle ich sogar, in Dateipfaden nur nicht akzentuierte Buchstaben, Ziffern und +-/._% Zeichen (mit /dem Verzeichnis-Trennzeichen) zu verwenden. "Versteckte" Dateien (beginnend mit .) sollten mit Vorsicht und Sparsamkeit verwendet werden. Ich glaube, dass die Verwendung von Leerzeichen in einem Dateinamen ein Fehler ist. Verwenden Sie stattdessen einen Unterstrich (zB foo/bar_bee1.txt) oder ein Minus (zB foo/bar-bee1.txt)

Basile Starynkevitch
quelle
Ja, ich weiß, dass ich ein Zeilenumbruchzeichen kopieren und einfügen kann, aber wie kann ich das ohne Kopieren und Einfügen tun?
AB
4
Hier gibt es kein Kopieren oder Einfügen. Es gibt eine getippte Newline danach foo.
Dan
Es funktioniert jedoch auch beim Kopieren und Einfügen
kos
1
Es ist eine schlechte Idee für übliche Dateien, aber ein guter Testfall, wie das OP erwähnte.
Artdanil
5
@und ,wäre harmloser als -(wie in der ersten Position) oder %. Alle diese Empfehlungen dienen hauptsächlich dazu, Fehler in einigen Anwendungen zu umgehen (die bei Dateinamen auftreten, die ansonsten aus Sicht des Betriebssystems gültig sind). Es fühlt sich rückständig an, die Leute zu bitten, diese Zeichen nicht zu verwenden, anstatt ihre Fehler zu beheben.
Stéphane Chazelas
18

Wenn Sie bash verwenden, sollte dieser Befehl funktionieren.

mv a $'b\nc'
Favadi
quelle
4
Funktioniert mit also zsh=)
AB
3
bash, wie zsh und FreeBSD sh kopierte das von ksh93.
Stéphane Chazelas
Vielen Dank! Ich habe $ in bash total vergessen, während es meine eigene Antwort auf SO war, es zu empfehlen
stackoverflow.com/questions/1825552/grep-a-tab-in-unix/…
6

Ich weiß, dass Sie nach einer mvLösung gefragt haben , aber trotz der Warnung ist dies leicht möglich rename(im Perl-Paket):

~/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
quelle
Vielen Dank für die renamesehr gute Idee. +1
AB
5

Ein Aspekt dieses Problems betrifft nicht wirklich awk- und nur ein wenig die Shell. Das Problem ist, dass auf einem standardmäßigen, kanonischen tty die meiste Zeit die tty-Disziplin des Kernels Ihre Eingaben puffert - sie wird nur auf Ihrem Bildschirm und nirgendwo anders wiedergegeben -, damit sie effizient mit Rückständen und Ähnlichem umgehen können.

Wenn Sie jedoch die Eingabetaste drücken oder auf andere Weise eine neue Zeile eingeben, werden alle diese gepufferten Daten auf einmal an die Leseanwendung gesendet - normalerweise an Ihre Shell. Sie können dies beobachten, indem Sie $PS2nach Eingabe eines baumelnden Zitats darauf achten. Wenn die Shell gedruckt wird, liegt dies $PS2daran, dass sie nur einen Block Ihrer Eingabe liest und noch nicht davon überzeugt ist, dass Sie fertig sind.

Der \nEinfachheit halber benötigen Sie also eine Möglichkeit, eine Ewline in den Terminalpuffer zu senden, ohne die gesamte andere Eingabe sofort verschieben zu müssen. Der üblicher Weg , dies zu tun ist , w / die Schlüsselsequenz CTRL+V- das Anführungszeichen für das Terminal Ihrer nächsten Eingabezeichen. Tun Sie es CTRL+Vdann CTRL+J- denn letzteres ist normalerweise das \nEingeben einer wörtlichen Ewline. Sie werden wissen, dass es funktioniert, wenn Sie es nicht sehen, $PS2weil die Shell Ihre Eingabe immer noch nicht gelesen hat.

Beachten Sie aber , dass , wenn es nicht gelesen , dass die Eingabe Ihrer früheren CTRL+Vüberhaupt keinen Unterschied für die Schale gemacht haben - , dass zitiert es nur für die Linie Disziplin. Sie sollten auf jeden Fall auch die Newline zitieren, um etwas Sinnvolles daraus zu machen.

Übrigens, CTRL+Vkann auf andere Weise sinnvoll angewendet werden - zum Beispiel "$(printf \\33)"ist dies nicht die einzige Möglichkeit, ein ESCZeichen in ein Shell-Skript zu schreiben - und es ist nicht einmal die einfachste. Sie können buchstäblich jedes Zeichen eingeben, das Ihre Tastatur sendet, ohne dass der Eingabetreiber versucht, es zu interpretieren, wenn Sie es zuerst auf diese Weise verlassen.

Ich verwende oft gerne <tab> s in der Befehlszeile, ohne dass die Shell versucht, etwas zu vervollständigen. Da Shells, die eine Vervollständigung ausführen, <tab> normalerweise so konfigurieren stty eol \t, dass ihre Vervollständigungssysteme CTRL+Vauch in unbekannten Umgebungen funktionieren, funktioniert dies für mich.

mikeserv
quelle
2
Vielen Dank, dass Sie 'Strg + V' erwähnt haben. Ich wollte gerade eine Antwort darauf posten und sah dann Ihre Erklärung.
Artdanil