Dodaj nowy wiersz do nazwy pliku za pomocą `mv`

11

To poważne pytanie. Testuję niektóre awkskrypty i potrzebuję plików z nową linią w nazwie.


Czy można dodać nowy wiersz do nazwy pliku za pomocą mv?

Teraz mogę to zrobić za pomocą touch:

touch "foo
bar"

Dotykając dodałem znak nowej linii na kopię i wklej. Ale nie mogę pisać fooReturnbarw mojej powłoce.

Jak mogę zmienić nazwę pliku, aby nazwa pliku zawierała nową linię?


Edytuj 2015/06/28; 19:08

Aby dodać nowy wiersz w zsh, mogę użyć Alt+Return

AB
źródło
2
Dlaczego pytasz? Żartujesz sobie ze znajomych?
Basile Starynkevitch
2
Nie usuwaj pytania, jest to przydatne. Ale naprawdę unikaj nowych wierszy w nazwach plików tak bardzo, jak to możliwe.
Basile Starynkevitch
2
Co sprawia, że ​​uważasz, że robienie tego mvbyłoby czymś innym niż robienie tego touch? Próbowałeś tego samego?
Kevin
2
Cóż, możesz skopiować i wkleić do mvpolecenia tak łatwo, jak touch.
Kevin
2
Więc pozwól, że zasugeruję ci wyraźniejsze sformułowanie OP: W danym środowisku powłoki nie mogę wpisać nowego wiersza w cytowanym ciągu, jak w echu „a [return] b”.
dan

Odpowiedzi:

22

To zły pomysł (mieć dziwne znaki w nazwach plików), ale możesz to zrobić

 mv somefile.txt "foo
 bar"

(mogłeś także zrobić mv somefile.txt "$(printf "foo\nbar")"lub mv somefile.txt foo$'\n'baretc ... szczegóły są specyficzne dla twojej powłoki. Używam zsh)

Przeczytaj więcej o globowaniu , np. Glob (7) . Szczegóły mogą być specyficzne dla powłoki. Ale zrozum, że /bin/mv jest to (przez twoją powłokę), poprzez execve (2) , rozszerzona tablica argumentów: rozwijanie i globowanie argumentów jest obowiązkiem powłoki wywołującej.

Możesz nawet napisać mały program C, aby zrobić to samo:

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

Zapisz powyższy program w foo.c, skompiluj go, a gcc -Wall foo.c -o foo następnie uruchom./foo

Podobnie, możesz napisać podobny skrypt w Perlu, Ruby, Python, Ocaml itp.

Ale to zły pomysł. Unikaj nowych linii w nazwach plików (wprowadzi użytkownika w błąd i może uszkodzić wiele skryptów).

W rzeczywistości polecam nawet używać nieakcentowanych liter, cyfr i +-/._% znaków (z / separatorem katalogów) w ścieżkach plików. Z plików „ukrytych” (zaczynających się od .) należy korzystać ostrożnie i bezstronnie. Uważam, że użycie dowolnego miejsca w nazwie pliku jest błędem. Zamiast tego użyj podkreślenia (np. foo/bar_bee1.txt) Lub minus (np. foo/bar-bee1.txt)

Basile Starynkevitch
źródło
Tak, wiem, że mogę skopiować i wkleić znak nowej linii, ale jak mogę to zrobić bez kopiowania i wklejania?
AB
4
Nie ma tu żadnej kopii ani wklejania. Po niej jest wpisana nowa linia foo.
dan
Działa to również kopiowanie i wklejanie
Kos
1
To zły pomysł na zwykłe pliki, ale dobry przypadek testowy, jak wspomniał PO.
artdanil
5
@i ,byłby bardziej nieszkodliwy niż -(jak na pierwszej pozycji) lub %. Wszystkie te zalecenia dotyczą głównie omijania błędów w niektórych aplikacjach (które psują nazwy plików, które w innym przypadku są prawidłowe z punktu widzenia systemu operacyjnego). Czuje się zacofanym, prosząc ludzi, aby nie używali tych postaci, zamiast naprawiać swoje błędy.
Stéphane Chazelas
19

Jeśli używasz bash, to polecenie powinno działać.

mv a $'b\nc'
favadi
źródło
4
Działa zshrównież z =)
AB
3
bash, podobnie jak zsh i FreeBSD sh, skopiował to z ksh93.
Stéphane Chazelas,
Dziękuję Ci bardzo! Całkowicie zapomniałem o $ w bashu, podczas gdy była to moja własna odpowiedź na SO, odradzając używanie go - stackoverflow.com/questions/1825552/grep-a-tab-in-unix/…
antimirov
6

Wiem, że poprosiłeś o mvrozwiązanie, jednak pomimo ostrzeżenia można to łatwo zrobić za pomocą rename(w pakiecie Perla):

~/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
źródło
Dziękuję za renamebardzo dobry pomysł. +1
AB
5

Jeden aspekt tego problemu tak naprawdę nie dotyczy awk- i tylko trochę o powłoce. Problem polega na tym, że na standardowym, kanonicznym tty przez większość czasu dyscyplina tty jądra buforuje twoje dane wejściowe - po prostu odbijają je na ekranie i nigdzie indziej - aby mógł skutecznie obsługiwać odstępy wsteczne i tym podobne.

Jednak gdy naciśniesz klawisz Return lub w inny sposób wprowadzisz nowy wiersz, wszystkie buforowane dane zostaną natychmiast przekazane do aplikacji do czytania - zwykle do powłoki. Możesz to zaobserwować, obserwując $PS2po wprowadzeniu wiszącego cytatu. Gdy powłoka drukuje $PS2, dzieje się tak, ponieważ po prostu odczytuje jakiś blok danych wejściowych i nie jest jeszcze przekonana, czy to koniec.

Tak więc, dla wygody, potrzebujesz jakiegoś sposobu wysłania \newline do bufora terminala bez konieczności natychmiastowego wypychania wszystkich innych danych wejściowych. Standardowym sposobem na to jest sekwencja klawiszy CTRL+V- która podaje terminalowi twój następny znak wejściowy. Zrób CTRL+Vto CTRL+J- ponieważ to drugie jest zwykle jak \nnapisać dosłowną ewline. Będziesz wiedział, że zadziałało, gdy nie widzisz, $PS2ponieważ powłoka nadal nie odczytała danych wejściowych.

Zauważ jednak, że jeśli nie przeczytałem, że wejście wcześniejszej CTRL+Vbędzie dokonały żadnego znaczenia dla powłoki w ogóle - że tylko cytuje go w składzie dyscypliny. Na pewno będziesz chciał zacytować nowy wiersz, a także zrobić z nim coś sensownego.

Nawiasem mówiąc, CTRL+Vmoże być użytecznie zastosowany na inne sposoby - na przykład "$(printf \\33)"nie jest to jedyny sposób na zapisanie ESCznaku w skrypcie powłoki - i nie jest nawet najprostszy. Możesz dosłownie wprowadzić dowolny znak, który wyśle ​​klawiatura, bez próby jego interpretacji przez sterownik wejściowy, jeśli po prostu w ten sposób uciec.

Często lubię używać <tab> w wierszu poleceń bez powłoki, która próbuje cokolwiek ukończyć. Ponieważ powłoki, które wykonują uzupełnianie, zwykle konfigurują <tab> w sposób synonimiczny stty eol \t, aby ich systemy uzupełniające działały, CTRL+Vdziała dla mnie nawet w nieznanym środowisku.

mikeserv
źródło
2
Dziękujemy za podanie „Ctrl + V”. Już miałem opublikować odpowiedź na ten temat, a potem zobaczyłem twoje wyjaśnienie.
artdanil