Add a newline into a filename with `mv`

12,449

Solution 1

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)

Solution 2

If you use bash, this command should work.

mv a $'b\nc'

Solution 3

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

Solution 4

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.

Share:
12,449

Related videos on Youtube

A.B.
Author by

A.B.

ZX81, C64, DR-DOS, OS/2, Linux Talk with me in Ask Ubuntu General Room profile for A.B. on Stack Exchange, a network of free, community-driven Q&amp;A sites http://stackexchange.com/users/flair/186154.png

Updated on September 18, 2022

Comments

  • A.B.
    A.B. almost 2 years

    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

    • Basile Starynkevitch
      Basile Starynkevitch almost 9 years
      Why do you ask? Are you making some joke or trick to a friend?
    • Basile Starynkevitch
      Basile Starynkevitch almost 9 years
      Don't remove the question, it is useful. But really avoid new lines in filenames as much as possible.
    • Kevin
      Kevin almost 9 years
      What makes you think doing it with mv would be any different from doing it with touch? Did you try the same thing?
    • A.B.
      A.B. almost 9 years
      @Kevin With touch I added the newline character per copy and paste. But I can't write "foo<return>bar" in my shell.
    • Kevin
      Kevin almost 9 years
      Well, you can copy and paste into the mv command just as easily as touch.
    • dan
      dan almost 9 years
      Why are you using copy and paste to type a newline?
    • dan
      dan almost 9 years
      I am using zsh. I can type "foo [return] bar" without problem. Please describe what is the problem with the standard [return].
    • A.B.
      A.B. almost 9 years
      @danielAzuelos That may be, I have used IFS to this day. Thx =)
    • dan
      dan almost 9 years
      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".
    • luca.vercelli
      luca.vercelli about 4 years
      This question is indeed very useful. Consider this situation. I have a Python script, and I use it both in Windows and Linux. The script has Windows-ending lines. It begins with "#/usr/bin/env python3\r" and Linux cannot handle that character "\r". So I created a symlink "python3\r" using these suggestions, and now the script is executable under Linux.
  • A.B.
    A.B. almost 9 years
    Yes, I know, that I can copy and paste a newline character, but how could I do that without copy and paste?
  • dan
    dan almost 9 years
    There isn't any copy or paste here. There is a typed newline after foo.
  • A.B.
    A.B. almost 9 years
    Works with zsh also =)
  • A.B.
    A.B. almost 9 years
    Thank you for the rename, a very good idea. +1
  • kos
    kos almost 9 years
    However it works also copy-pasting it
  • artdanil
    artdanil almost 9 years
    It is a bad idea for usual files, but a good test case, as the OP mentioned.
  • artdanil
    artdanil almost 9 years
    Thank you for mentioning 'Ctrl+V'. I was about to post answer about it and then saw your explanation.
  • Stéphane Chazelas
    Stéphane Chazelas almost 9 years
    bash, like zsh and FreeBSD sh copied that from ksh93.
  • Basile Starynkevitch
    Basile Starynkevitch almost 9 years
    Of course / is the directory separator
  • Basile Starynkevitch
    Basile Starynkevitch almost 9 years
    Why do you believe % is more harmful than @ (which has special meaning, e.g. for scp)?
  • antimirov
    antimirov almost 5 years
    Thank you very much! I totally forgot about $ in bash, while it was my own answer on SO advising using it - stackoverflow.com/questions/1825552/grep-a-tab-in-unix/…
  • mekb
    mekb almost 3 years
    Why does ''$'\n'' show in ls?
  • mekb
    mekb almost 3 years
    ohh the $ denotes escaped characters