1

I have some .xls files in a defined directory (say in /A/B). I want to rename top 10 latest files and append "-bkp" in their names.

I tried, not working

ls -lt *.xls | head -1 | awk '{print "mv " $9 " "$9-bkp}' | sh

I tried find and -exec but how do we get top l0 latest modified files

2

You should use -10 and not -1 as argument to head, and you also need quotes around -bkp, so

 ls -lt *.xls | head -10 | awk '{print "mv " $9 " "$9"-bkp"}' | sh

should work. And you would probably have realised if you had tried removing | sh, so the command just ends with awk printing the commands.

1

If you don't want to make any assumption on what character filenames may contain, you could do:

ls -dt ./*.xls | awk -v q="'" -v n=10 '
  function process() {
    if (NR > 1) {
      gsub(q, q "\\" q q, file)
      print "mv " q file q, q file "-bkp" q
      if (!--n) exit
    }
  }
  /\// {
    process()
    file = $0
    next
  }
  {file = file "\n" $0}
  END  {process()}' | sh -x

Or if you have zsh:

for f (*.xls(om[1,10])) cp -- $f $f-bkp

Or

autoload zmv
zmv -C '*.xls(#qom[1,10])' '$f-bkp'
0

If you have ksh93 (for the arrays) and perl (for the timestamp/stat), then this will work:

files=(*.xls)
# exit early if there are no matching files
[ "$files" = "*.xls" ] && exit 0

for index in ${!files[@]}
do
  t[$index]=$(perl -e '$x=(stat(shift))[9]; print "$x"' "${files[index]}")
done

for i in ${!t[@]}
do
  printf "%d %d\n" ${t[i]} $i
done | sort -rn  | head -10 | while read t i
do
  mv "${files[i]}" "${files[i]}-bkp"
done

It works by gathering the list of *.xls files, then loops over those files, calling the perl one-liner to print the last-modified timestamp of each file, saving the result into a parallel array t.

It then loops over t, printing the timestamp and the array index, pipes that to sort to get the most-recently modified files to the beginning, pipes that to head to get the top 10, then loops back over that output to call the mv command on the corresponding files.

-2

ls has a "sort"-parameter that can take a value of "time".

#!/bin/bash
IFS=$'\n'
for file in $(ls *.xls --sort=time|head -n 10);
do
    mv $file $file-bkp
done
unset IFS

The IFS-shenanigans are because the for-loop becomes ill-mannered if you have spaces in your filenames. I believe there are more orthodox solutions to that issue, but my solution works. You can remove the lines containing IFS if you know for a fact that your filenames contain no spaces.

  • That ls command doesn't work on AIX. It's just 'ls -t` to sort by time (last modified). – EightBitTony May 16 '16 at 10:28
  • And -t also works on Linux. – Henrik May 16 '16 at 10:42

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.