2009-10-19 5 views
10

J'ai un répertoire avec des fichiers qui ressemblent à ceci:BASH: Trouver le nom de fichier numéro le plus élevé dans un répertoire où les noms commencent par des chiffres (ls, sed)

001_something.php 002_something_else.php 
004_xyz.php  005_do_good_to_others.php 

Je veux finalement créer un nouveau, PHP vide fichier dont le nom commence par le numéro suivant de la série.

LIST=`exec ls $MY_DIR | sed 's/\([0-9]\+\).*/\1/g' | tr '\n' ' '` 

Le code précédent me donne une chaîne comme ceci:

LIST='001 002 004 005 ' 

Je veux saisir cette 005, incrément par un, puis utiliser ce numéro pour générer le nouveau nom de fichier. Comment est-ce que je fais cela dans BASH?

+0

Est-ce à exécuter dans le cadre d'une application web? Si le serveur Web accepte plusieurs requêtes simultanées (la plupart le font), comment allez-vous vous protéger contre la condition de concurrence lorsque les processus A et B effectuent la vérification simultanément et créent ensuite un fichier avec le même numéro? – telent

+0

Non, cela faisait partie d'un script shell pour émuler l'outil 'generate migration' des rails, pour une application php qui utilise une implémentation rousse-stepchild du modèle de migration de base de données. – Jake

Répondre

10
$ LIST=($LIST) 
$ newfile=`printf %03d-whatever $((10#${LIST[${#LIST}]}+1))` 
$ echo $newfile 
006-whatever 

Donc, c'est spécifique à bash. Ci-dessous est une solution any-posix-shell-including-bash, alors j'imagine que quelque chose de plus simple est possible.

$ cat /tmp/z 
f() { 
    eval echo \${$#} | sed -e 's/^0*//' 
} 
LIST='001 002 004 005 ' 
newname=`printf %03d-whatever $(($(f $LIST) + 1))` 
echo $newname 
$ sh /tmp/z 
006-whatever 
$ 
+0

ne fonctionne pas avec '008': –

+0

C'est parce que les nombres commençant par "0" sont * octal *. – paxdiablo

+0

Ok, bug octal corrigé. – DigitalRoss

0

est ici une solution (vous avez besoin bc):

#!/bin/bash 

LIST='001 002 008 005 004 002 ' 
a=0 

for f in $LIST 
do 
    t=$(echo $f + 1|bc) 
    if [ $t -ge $a ] 
    then 
     a=$t 
    fi 
done 

printf "%03d\n" $a 
+0

Vous pouvez également utiliser 'dc': t = $ (echo $ f 1 + pq | dc), ou' expr': t = $ (expr $ f + 1), ou bash math: t = $ (($ f + 1)) – mouviciel

1
$ for file in *php; do last=${file%%_*} ; done 
$ newfilename="test.php" 
$ printf "%03d_%s" $((last+1)) $newfilename 
006_test.php 

je vous laisse le soin de faire la création du nouveau fichier

11

Avez-vous besoin de la totalité de la liste?

Si non

LAST=`exec ls $MY_DIR | sed 's/\([0-9]\+\).*/\1/g' | sort -n | tail -1` 

vous donnera juste la partie 005 et

printf "%03d" `expr 1 + $LAST` 

affichera le numéro suivant dans la séquence.

+0

ou printf "% 03d" $ ((1 + $ LAST)) – leedm777

2

rapide, sans sous-shell et sans boucle (poignées aussi les noms de fichiers avec des espaces) *:

list=([0-9]*) 
last=${list[@]: -1} 
nextnum=00$((10#${last%%[^0-9]*} + 1)) 
nextnum=${nextnum: -3} 
touch ${nextnum}_a_new_file.php 

Étant donné votre exemple fichiers la sortie serait:

006_a_new_file.php 
7

En utilisant seulement des outils standard, ce qui suit vous donnera le préfixe pour le nouveau fichier (006):

 
ls [0-9]* | sed 's/_/ _/' | sort -rn | awk '{printf "%03d", $1 + 1; exit}' 
+1

si le fichier 001 n'existe pas, cette commande ne devrait-elle pas imprimer 001? – Stefan

+0

Cela ne fonctionne pas sur les répertoires contenant beaucoup, beaucoup de fichiers - la sortie est: bash:/bin/ls: Liste d'arguments trop long –

+0

@John: Remplacez simplement les 'ls [0-9] *' par 'ls | grep^[0-9] '. – Idelic

3

Cela semble être plus simple.

ls [0-9]* | sort -rn | awk '{FS="_"; printf "%03d_new_file.php\n",$1+1;exit}' 
1
  1.  
    touch "newfile_$(printf "%03d" $(echo $(ls 00?_*.php|sed 's/_.*//'|sort -rn|head -1)+1|bc))" 
    

    2.

    num=$(ls 00?*.php|sed 's/.*//'|sort -rn|head -1) 
    touch $(printf "newfile_%03d" $((num+1)))

Questions connexes