Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have some files in a directory that are named IMG_0001.jpg ... IMG_9999.jpg. I want to move to another directory the files that lexicographically have a name greater than IMG_9431.jpg How can I do that?

share|improve this question
add comment

2 Answers

up vote 4 down vote accepted

You can use sort and sed to get a list of files grater than some-string like this:

$ ls -v
0?#Li  23?24  E.See  NULib  Yoush  ce-Su  edint  ethat  ibble  itwil  lines  of16   plesA  ryGen  t6?#C  witht  #ver
0?#mo  25?i   Examp  NYWAR  along  cribb  edist  ev     ibrar  ix B.  mapfo  ofthe  ppend  sdist  tetot  y8?#9  (atyo
0,22   27?#   FORAP  NextA  areFo  dacop  edwar  frees  ic11   lPubl  mored  oftwa  publi  sefor  theGN  yGene  )1995
1.scr  02111  Finla  Peter  aryis  datio  eful,  ftheG  ight(  landJ  mport  on23#  ralPu  se,or  tunde  yofth  ,USA.
1-200  ARTIC  GIMPT  RANTY  avere  difyi  eitan  ftwar  imbal  lbeus  ncerK  on,MA  raryG  shedb  t,wri  ytheF  ;ifno
2John  Appen  HANTA  Softw  bleof  dix B  enera  ght(C  impli  ld7?#  nc.,5  oolki  raryi  simpl  ublic  #19?#  ;with
4?#Th  BILIT  HOUTA  TNESS  blic2  e.py4  enthe  gtk26  ingar  lePla  ngpix  opyri  rdraw  sion.  ucanr  #Bost   Code
5?#GT  Backi  K-The  U17?#  brary  e.pyB  eralP  he21   ion)a  lesPr  nseas  oshMa  reeSo  sion2  undat  #Free
9Temp  Conte  Libra  ULARP  cDona  eExam  ermso  hehop  ion,I  lesTa  ntsB.  ouldh  re;yo  slibr  uropt  #Lice
13?#1  Copyr  Licen  URPOS  ceive  eFoun  erver  her12  islib  lescr  nylat  outev  ribut  s,Spe  utWIT  #MERC
15?#b  C)200  Matti  YorFI  cense  eGNUL  etail  hisli  ite33  lesim  n;eit  ple.p  rthet  s.18   vpyth  #Thi

$ mkdir greater-than-sion

Here is the magic:

$ find -type f -print0 | 
  sort -z |
  sed -z '1,/sion/d' | 
  xargs -0 mv -t greater-than-sion

Lines:

  1. print a list of files, separated by NUL instead of newlines (-print0)
  2. sort them
  3. delete lines lower than (inclusive) some string (here sion) – note that this only works with GNU sed, which implements a -z option to parse NUL-terminated input
  4. pass this list to mv with xargs

And the desired result:

$ ls -R
.:
 Code  ;ifno  25?i   avere  Conte  edist  erver  GIMPT               ic11   K-The  lines  nseas  opyri  raryG  se,or
#19?#  ;with  27?#   Backi  Copyr  edwar  etail  greater-than-sion/  ight(  landJ  lPubl  ntsB.  oshMa  raryi  sefor
#Bost  0?#Li  2John  BILIT  cribb  eExam  ethat  gtk26               imbal  lbeus  mapfo  NULib  ouldh  rdraw  shedb
#Free  0?#mo  4?#Th  bleof  dacop  eFoun  ev     HANTA               impli  ld7?#  Matti  nylat  outev  re;yo  simpl
#Lice  0,22   5?#GT  blic2  datio  eful,  Examp  he21                ingar  lePla  mored  NYWAR  Peter  reeSo  sion.
#MERC  02111  9Temp  brary  difyi  eGNUL  Finla  hehop               ion)a  lescr  mport  of16   ple.p  ribut
#Thi   1.scr  along  C)200  dix B  eitan  FORAP  her12               ion,I  lesim  n;eit  ofthe  plesA  rthet
#ver   1-200  Appen  cDona  e.py4  enera  frees  hisli               islib  lesPr  nc.,5  oftwa  ppend  ryGen
(atyo  13?#1  areFo  ceive  e.pyB  enthe  ftheG  HOUTA               ite33  lesTa  ncerK  on,MA  publi  s,Spe
)1995  15?#b  ARTIC  cense  E.See  eralP  ftwar  ibble               itwil  Libra  NextA  on23#  ralPu  s.18
,USA.  23?24  aryis  ce-Su  edint  ermso  ght(C  ibrar               ix B.  Licen  ngpix  oolki  RANTY  sdist

./greater-than-sion:
sion2  Softw  t6?#C  theGN  tunde  ublic  ULARP  uropt  utWIT  witht  yGene  YorFI  ytheF
slibr  t,wri  tetot  TNESS  U17?#  ucanr  undat  URPOS  vpyth  y8?#9  yofth  Yoush
share|improve this answer
    
Nice one. I totally forgot about sed here. –  slhck Sep 16 '13 at 10:59
    
What does 'sed -z' do? Did a quick check and couldn't find the explanation for option -z. –  jaychris Sep 16 '13 at 18:23
    
Processes lines separated by the null character instead of the newline character. This is stated in the manual. –  Ярослав Рахматуллин Sep 16 '13 at 20:40
    
Note that is is a GNU-only option, thus not BSD sed. (cc @jaychris) –  slhck Sep 17 '13 at 12:52
    
Thanks Ярослав & slhck. I tried the manual and googled but didn't get this option. Seems like the sed I accessed is not GNU version. –  jaychris Sep 19 '13 at 15:58
show 1 more comment

With brace expansion, which is available in Bash 3 and above, as well as Zsh and several other shells:*

mv IMG_{9431..9999}.jpg some_other_dir

The braces will be expanded to all the numbers between 9431 and 9999, so it is equivalent to writing out this:

mv IMG_9431.jpg IMG_9432.jpg … IMG_9999.jpg some_other_dir

This will fail if there are too many files (see this post about ARG_MAX for background info.)

If your shell lacks brace expansion features, or you have too many files, you can do this – which could be a little slower:

for n in $(seq 9431 9999); do mv "IMG_$n.jpg" some_other_dir; done

If you really want to sort lexicographically, have a look at Ярослав Рахматуллин's answer. Much nicer than what follows here.

You'll have to sort your file names somehow. This is what I came up real quick, and it's not pretty and far from ideal. It works with GNU/Linux (grep, sort, xargs) and deals with any file names, including quotes and whitespace.

tmp="$(mktemp /tmp/files.XXX)"
find . -type f -name 'IMG*'  -maxdepth 1 -print0 | sort -z > "$tmp"
line=$(grep -nz IMG_9984.jpg "$tmp" | cut -d: -f1)
tr '\0\n' '\n\0' < "$tmp" | tail -n "+$line" | tr '\0\n' '\n\0' |
xargs -0 -I{} echo mv {} some_other_dir
rm "$tmp"

Remove the echo when you're sure this does what you want. What we do here:

  • Create a temporary file to hold the file names.

  • Find all files matching the pattern, and sort them into a temporary file. The records are delimited by NUL characters (-print0, -z) so we can deal with any file name.

  • Find the "line" number of the file name, e.g. IMG_9984.jpg

  • Swap NUL and newline in the temporary file so tail can deal with it.

  • Swap them back so xargs can deal with it (-0) and mv the files to the other directory.

This would be easier if we didn't have to deal with files that contain quotes or whitespace, but… that's just me. Better be safe than sorry.

share|improve this answer
    
Perhaps note that it is Bash specific (?). A more exotic note is that it will fail if one expands to more than ARG_MAX arguments in total. A couple of orders of magnitude to spare in this case, though. –  Daniel Andersson Sep 16 '13 at 9:09
    
@DanielAndersson I was going to add a solution for older / alternative shells. It's in Bash 3 and above, also in Zsh and ksh, possibly others. –  slhck Sep 16 '13 at 9:12
    
Just for the knowledge, how would i do it if there was not a pattern in the files and I just wanted to get all the files "lexicographically greater than" a specific filename? –  Paralife Sep 16 '13 at 9:49
    
@Paralife Could be tricky – see my updated answer. But Ярослав's solution is definitely more straightforward. –  slhck Sep 16 '13 at 10:58
    
ok what is right here? Give accepted answer to Ярослав's more generic solution that might be useful to other situations also, or leave this accepted answer since it solves fine the specific question I asked and is simpler? –  Paralife Sep 16 '13 at 18:30
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.