1

I have a list I want to change from a script. I want to move top entry to bottom so I get a rotating list each time the script is run. I also want to append date to the line that got moved to bottom. from this. The top folder will be used to store old backups too using rclone.

old1
old2
old3

to this

old2
old3
old1 date time

I found a code I tried to use, but it wont move the top entry, just copy it to bottom so I get an output like this

old1
old2
old3
old1 date time
old1 date time

The code I tried # Rotate the BACKUPLIST-file, deleting the top entry and adding a new entry at the bottom
OLDEST=$(head -1 $BACKUPLIST | sed 's/ .*//') # The oldest, extra backup-version COUNT=$(cat $BACKUPLIST | wc -l) # Number of files in list
((COUNT=$COUNT-1)) tail -$COUNT $BACKUPLIST > $BACKUPLIST.tmp echo $OLDEST $(date) >> $BACKUPLIST.tmp # Add a new line to the bottom, including today's date

4 Answers4

2

It's a little tricky to get the escaping correct in the date format string, but given

$ cat file
old1
old2
old3

then

$ cat << 'EOF' | ed -s file
1m$
r !date +\ \%x\ \%X
.-1,.j
,p
EOF
old2
old3
old1 2019-07-25 05:42:32 PM

or (as a one-liner)

printf '%s\n' '1m$' 'r !date +\ \%x\ \%X' '.-1,.j' ',p' | ed -s file

To edit the file in place, replace p (print) by wq (write-quit). You can replace %x and %X with other format specifiers of your choice of course.

steeldriver
  • 81,074
1
$ date +"$(head -n 1 file) %F %T" >>file; sed -i 1d file
$ cat file
old2
old3
old1 2019-07-25 23:39:51
$ date +"$(head -n 1 file) %F %T" >>file; sed -i 1d file
$ cat file
old3
old1 2019-07-25 23:47:10
old2 2019-07-25 23:47:22

This inserts the first line of file as a string into the output format of date. The %F is a shorthand for %Y-%m-%d and %T is a shorthand for %H:%M:%S in most date implementations.

The output of date is then appended to the end of file.

After appending the new data, sed is used to delete the old first line (using an in-place edit). Some sed implementation has to be invoked as sed -i '' 1d file (see How can I achieve portability with sed -i (in-place editing)?).


The fourth run of the commands above would yield

$ date +"$(head -n 1 file) %F %T" >>file; sed -i 1d file
$ cat file
old2 2019-07-25 23:47:22
old3 2019-07-25 23:51:22
old1 2019-07-25 23:47:10 2019-07-25 23:51:24

If this is not wanted, use something like

awk '{ print $1; exit }'

in place of head -n 1. This would pick out the first whitespace-delimited column on the first line, but not the full first line with the old date and time.

Kusalananda
  • 333,661
0

You can do this quickly in bash:

mapfile -t lines < file
printf "%s\n" "${lines[@]:1}" "${lines[0]} $(date "+%F %T")" > file

Putting that into a function:

rotate_file() {
    [[ -w "$1" ]] || return 1
    local -a lines
    mapfile -t lines < "$1"
    printf "%s\n" "${lines[@]:1}" "${lines[0]} $(date "+%F %T")" > "$1"
}

A demo:

$ cat file
1
2
3
$ rotate_file file
$ cat file
2
3
1 2019-07-25 17:16:51
$ rotate_file file
$ cat file
3
1 2019-07-25 17:16:51
2 2019-07-25 17:16:57

This slurps the whole file into memory. If the file is large, this can be slow.

glenn jackman
  • 85,964
0
$ hd="$(head -1 file)"; tail +2 file; printf '%s %s\n' "$hd" "$(date)"
old2
old3
old1 Thu, Jul 25, 2019  4:27:15 PM

$ hd="$(head -1 file)"; { tail +2 file; printf '%s %s\n' "$hd" "$(date)"; } > tmp && mv tmp file; cat file
old2
old3
old1 Thu, Jul 25, 2019  4:28:44 PM

$ hd="$(head -1 file)"; { tail +2 file; printf '%s %s\n' "$hd" "$(date)"; } > tmp && mv tmp file; cat file
old3
old1 Thu, Jul 25, 2019  4:28:44 PM
old2 Thu, Jul 25, 2019  4:28:46 PM

$ hd="$(head -1 file)"; { tail +2 file; printf '%s %s\n' "$hd" "$(date)"; } > tmp && mv tmp file; cat file
old1 Thu, Jul 25, 2019  4:28:44 PM
old2 Thu, Jul 25, 2019  4:28:46 PM
old3 Thu, Jul 25, 2019  4:28:48 PM
Ed Morton
  • 31,617