Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Command substitution in sed

Tags:

bash

sed

I want to read the first six characters of a text file into a string and prepend every other non-blank line in that file with that string. An example of such file could be:

04/17 Walmart .toys $ 70 .cash $ -70

Caltex .gas 20 $ .cheque $ -20

McDonalds .burger 1 $ .cash $ -1

Each entry, i.e.: each non-empty line, needs a date, which for reasons of easy data entry has been entered on the first line only. Entries are separated by 1 or more empty lines. The output would look like this:

04/17 Walmart .toys $ 70 .cash $ -70

04/17 Caltex .gas 20 $ .cheque $ -20

04/17 McDonalds .burger 1 $ .cash $ -1

I can match the non empty strings with things like ^[^@]+[ ]*.[ ]([^;{}:]+)[ ]*$, but I don't know how to actually implement that for non blank lines.

This Bash script looks attractive to me, but I don't know how then to insert my string at the start.

I also can't find the straight answer to my question on Stack Overflow.

I tried a script which accepts a file name:

read -n 6 date < $1
sed 's/^/$(echo $date)/' | \
sed 's/^$(echo $date)\n//' | > $newName

I was able to come up with prepending the date with space (e.g. the string: '04/17 ') to every line, and then remove the same from every line in which nothing follows.

However, it seems that sed doesn't accept the command substitution:

sed: -e expression #1, char 10: unknown option to `s'
like image 799
HarryH Avatar asked Dec 08 '22 18:12

HarryH


2 Answers

You should be able to do this with one sed command:

read -rn 6 date < "$1"
sed -E 's#^([a-zA-Z]+)#'"$date"' \1#g' "$1" > newfile

The capture group ensures there's at least one character on the line before inserting the date.

EDIT: Based on the revision to your question:

newfile="output.txt"
lineone=$(head -1 "$1");

read -rn 6 date <<< "$lineone"
sed -E 's#^([a-zA-Z]+)#'"$date"' \1#g; 1s#^.*$#'"$lineone"'#' "$1" > "$newfile" 

Since you aren't doing an in-place edit, you can do the $date insertions, and then go back and swap out the first line since it would end up with two dates. There might be "better" ways to do this such as using Perl, or losing the second sed command, although this should at least give you a basic idea though on how it works...

Result (newfile):

04/17 Walmart .toys $ 70 .cash $ -70

04/17 Caltex .gas 20 $ .cheque $ -20

04/17 McDonalds .burger 1 $ .cash $ -1

NOTE: In some versions of sed the option for extended regex can either be -r or -E.

like image 94
l'L'l Avatar answered Jan 03 '23 13:01

l'L'l


Pure bash answer:

unset n
while read -r x ; do
    case "${#n}$x" in 6) ;; 6*) x="$n$x" ;; *) n="${x:0:6}" ;; esac
    echo "$x"
done < file > newfile

Output:

04/17 Walmart .toys $ 70 .cash $ -70

04/17 Caltex .gas 20 $ .cheque $ -20

04/17 McDonalds .burger 1 $ .cash $ -1
like image 45
agc Avatar answered Jan 03 '23 14:01

agc