0

Please could someone check if my GNU sed script is the nice way to do it or if it's some sort of bad / wrong hack :p

$ ls -xN
1
2
3
4
$ ls -xN | sed -E "s/(.{1})$/\1\.log/" | sed -E "s/^(.{1})/Day\ \1/"
Day 1.log
Day 2.log
Day 3.log
Day 4.log

I can't understand why sed does not recognize \n (newline) and \s (whitespace):

  • NEWLINE:
    • I'd like to use \n, but I have to use .{1}$ instead
  • WHITESPACE:
    • I'd like to use \s, but I have to use \ instead
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
T. Caio
  • 129
  • 1
    What do you want to use newlines for? The sed expression won't see any newlines. As for the space, why would you want to use \s in the replacement text? You should be able to use just an ordinary space there. If you want to appond something to the end of the line, just do s/$/something/, and s/^/something/ for the start, and use -e to apply two separate expressions with a single sed command. – Kusalananda Aug 22 '22 at 12:16
  • 1
    But if you want to change the name of files, using rename would be the better option. See also Why *not* parse `ls` (and what to do instead)? – Kusalananda Aug 22 '22 at 12:20
  • Also note that if you have the names 1, 2, 3, 4 in a directory, then ls -xN (using GNU ls) would produce a single line of output (1 2 3 4), not a column of four rows. – Kusalananda Aug 22 '22 at 12:27
  • 1
    How about printf 'Day %s.log\n' * instead? – Stéphane Chazelas Aug 22 '22 at 13:57
  • @Kusalananda About newlines (\n): I was thinking that could be a nicer approach to tell sed to go to the end of the line. – T. Caio Aug 22 '22 at 17:28
  • @Kusalananda About rename: I'm forcing to use sed because I want to learn it by doing :) – T. Caio Aug 22 '22 at 17:32
  • @StéphaneChazelas <<How about printf 'Day %s.log\n' * instead?>> it works like a charm!! :) But I'm forcing to use sed to learn it by doing! ;) Anyway... Could you please elaborate on why it works with printf? What is printf doing? – T. Caio Aug 22 '22 at 17:51
  • @T.Caio One thing to learn is to use the right tool for the job. The sed editor is the right tool for processing lines of text. A filename is not a line of text as it may contain embedded newlines. – Kusalananda Aug 22 '22 at 18:12

1 Answers1

2

GNU sed recognizes both \n and \s. However, \s means "any whitespace character", it matches at least a space, a newline (aka line feed), a tab, a form feed, a vertical tab and a carriage return, but likely more in your locale. It is equivalent to the standard [[:space:]] character class. Since it is not one character but many, it cannot be used on the right hand side of the replacement operator since it is ambiguous and sed wouldn't know what you want to insert. It works fine on the left hand side though:

$ printf '[space]: [tab]:\t[CR]:\r[FF]\f\n'
[FF]ce]: [tab]: [CR]:

$ printf '[space]: [tab]:\t[CR]:\r[FF]\f\n' | sed 's/\s/MATCH/g' [space]:MATCH[tab]:MATCH[CR]:MATCH[FF]MATCH $

Also, you can use a simple space, there is no need to escape it:

$ printf '%s\n' {1..4} | sed -E 's/^(.{1})/Day \1/'
Day 1
Day 2
Day 3
Day 4

Next, GNU sed recognizes newline characters just fine, I just don't understand how they are relevant here. The $ means "match the end of the line". You could replace that with a \n if you want. For example:

$ printf '%s\n' {1..4} | sed -zE 's/(.)\n/\1.log\n/g'
1.log
2.log
3.log
4.log

It just doesn't seem relevant to the user case in your question. Finally, you could do the whole thing in a single operation:

$ printf '%s\n' {1..4} | sed -E 's/(.)/Day \1.log/'
Day 1.log
Day 2.log
Day 3.log
Day 4.log

On a final note, it is almost never a good idea to parse the output of ls as that will break on even slightly strange file names. Not really relevant in the specific case you mention, but you should avoid it in general.

terdon
  • 242,166