10

It's a serious question. I test some awk scripts and I need files with a newline in their names.


Is it possible to add a newline into a filename with mv?

I now, I can do this with touch:

touch "foo
bar"

With touch I added the newline character per copy and paste. But I can't write fooReturnbar in my shell.

How can I rename a file, to have a newline in the filename?


Edit 2015/06/28; 07:08 pm

To add a newline in zsh I can use, Alt+Return

  • 2
    Why do you ask? Are you making some joke or trick to a friend? – Basile Starynkevitch Jun 28 '15 at 11:27
  • 2
    Don't remove the question, it is useful. But really avoid new lines in filenames as much as possible. – Basile Starynkevitch Jun 28 '15 at 12:18
  • 2
    What makes you think doing it with mv would be any different from doing it with touch? Did you try the same thing? – Kevin Jun 28 '15 at 16:42
  • 2
    Well, you can copy and paste into the mv command just as easily as touch. – Kevin Jun 28 '15 at 17:10
  • 2
    So let me suggest you to formulate more clearly your OP: Within a given shell environment I can't type a newline within a quoted string as in echo "a[return]b". – dan Jun 30 '15 at 19:50
21

It is a bad idea (to have strange characters in file names) but you could do

 mv somefile.txt "foo
 bar"

(you could also have done mv somefile.txt "$(printf "foo\nbar")" or mv somefile.txt foo$'\n'bar, etc... details are specific to your shell. I'm using zsh)

Read more about globbing, e.g. glob(7). Details could be shell-specific. But understand that /bin/mv is given (by your shell), via execve(2), an expanded array of arguments: argument expansion and globbing is the responsibility of the invoking shell.

And you could even code a tiny C program to do the same:

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

Save above program in foo.c , compile it with gcc -Wall foo.c -o foo then run ./foo

Likewise, you could code a similar script in Perl, Ruby, Python, Ocaml, etc....

But that is a bad idea. Avoid newlines in filenames (it will confuse the user, and it could break many scripts).

Actually, I even recommend to use only non-accentuated letters, digits, and +-/._% characters (with / being the directory separator) in file paths. "Hidden" files (starting with .) should be used with caution and parcimony. I believe using any kind of space in a file name is a mistake. Use an underscore instead (e.g. foo/bar_bee1.txt) or a minus (e.g. foo/bar-bee1.txt)

  • Yes, I know, that I can copy and paste a newline character, but how could I do that without copy and paste? – A.B. Jun 28 '15 at 11:06
  • 4
    There isn't any copy or paste here. There is a typed newline after foo. – dan Jun 28 '15 at 11:07
  • However it works also copy-pasting it – kos Jun 28 '15 at 11:28
  • 1
    It is a bad idea for usual files, but a good test case, as the OP mentioned. – artdanil Jun 29 '15 at 6:41
  • 4
    @ and , would be more harmless than - (like in first position) or %. All those recomendations are mainly to work around bugs in some applications (that break on filenames that are otherwise valid from the OS point of view). It feels backward to ask people to not use those characters rather than fixing their bugs. – Stéphane Chazelas Jun 29 '15 at 20:17
18

If you use bash, this command should work.

mv a $'b\nc'
6

I know you asked for a mv solution, however, despite the warning, this can be easily done with rename (in the Perl package):

~/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
  • Thank you for the rename, a very good idea. +1 – A.B. Jun 28 '15 at 11:17
5

One aspect of this problem isn't really about awk - and only a little bit about the shell. The problem is that on a standard, canonical tty most of the time the kernel's tty discipline is buffering your input - just echoing it to your screen and nowhere else - so that it can efficiently handle backspacing and such-like.

However, when you press return or otherwise enter a newline, all of that buffered data is pushed at once to the reading application - usually your shell. You can observe this by watching for $PS2 after entering a dangling quote. When the shell prints $PS2 it's because its just read some block of your input and is not yet convinced you're through.

So, for convenience, what you need is some way of sending a \newline into the terminal buffer without having to push all of that other input immediately. The standard way to do so is w/ the key-sequence CTRL+V - which quotes for the terminal your next input character. Do CTRL+V then CTRL+J - because the latter is usually how to type a literal \newline. You'll know it's worked when you don't see $PS2 because the shell still hasn't read your input.

Note though that when it does read that input your earlier CTRL+V will have made no difference for the shell at all - that only quotes it for the line-discipline. You'll definitely want to shell-quote the newline as well to do anything meaningful with it.

By the way, CTRL+V can be usefully applied in other ways - for example "$(printf \\33)" is not the only way to write an ESC character into a shell script - and it isn't even the most simple. You can literally enter any character your keyboard will send without the input driver attempting to interpret it if you just first escape it in this way.

I often like to use <tab>s on the command-line without the shell attempting to complete anything. Because shells which do completion will typically configure <tab> in a way synonymous to stty eol \t, to make their completion systems work, CTRL+V works for me even in unfamiliar environments.

  • 2
    Thank you for mentioning 'Ctrl+V'. I was about to post answer about it and then saw your explanation. – artdanil Jun 29 '15 at 6:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.