Comment puis-je utiliser des caractères génériques de style ms-dos avec ls et mv?

9

J'ai le malheur de venir d'un arrière-plan MS-DOS - mais au moins cela me fait apprécier à quel point Linux est beaucoup plus puissant. J'ai travaillé pour obtenir mon Linux-Fu à la hauteur, mais il y a quelques choses qui pourraient être faites avec DOS que je ne sais pas comment accomplir le plus facilement avec Linux:

Renommer plusieurs fichiers - à l'aide de deux caractères génériques

c:\> dir

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt

c:\>rename *.txt *.bak

c:\> dir

Directory of c:\
    file1.bak
    file2.bak
    file3.bak
    file4.bak

Je sais que je pourrais utiliser find -execici, mais est-il possible d'utiliser une syntaxe plus courte - peut-être mvavec des drapeaux ou syntaxe spéciaux? Je suppose que la clé est le deuxième * caractère générique car Linux ne devrait pas avoir de problème avec le premier (c'est-à-dire que je sais comment sélectionner les fichiers que je veux renommer en utilisant des caractères génériques)

Renommer un fichier unique - à l'aide d'un caractère générique

c:\> dir

Directory of c:\
    file1.txt

c:\>rename file1.txt *.bak

c:\> dir

Directory of c:\
    file1.bak

Cela serait particulièrement utile lors du renommage de noms de fichiers longs et lourds. J'ai pensé que je pourrais peut-être utiliser mv file1.txt $1.bakpour finir avec file1.txt.bakce qui serait également acceptable, mais je ne suis pas sûr que vous puissiez référencer un $1paramètre en ligne avec une commande shell. Encore une fois, dans ce cas particulier, il est juste pratique de savoir comment ms-dos bastardise le *caractère générique à utiliser comme une sorte de correspondance capture / remplacement pour une partie du nom de fichier.

Filtrage des listes d'annuaire avec un caractère générique

c:\> dir

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt
    text.txt
    \temp       (directory) 

c:\> dir file*

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt

c:\> t*

Directory of c:\
    text.txt
    \temp       (directory) 

Je ne sais pas quelle est la bonne syntaxe pour le faire ls, ou si c'est même possible. Si j'ai fait quelque chose comme ls t*ça, il va récursivement dans les répertoires commençant par t. Ma solution de contournement a été utilisée find . --max-depth 1 -iname "t*"ou quelque chose du genre ls -al | grep t- ni l'une ni l'autre n'est aussi courte et simple que cela dir t*.

Enfin, je sais que je peux configurer des alias pour raccourcir ces longues commandes, mais j'aimerais apprendre quelques linux-fu prêts à l'emploi pour faire ces choses parce que parfois vous êtes connecté à un système distant ou que vous travaillez sur une nouvelle machine.

Alors, comment puis-je mvet les lsfichiers de la même manière que je le faisais diret les renamefichiers?

cwd
la source

Réponses:

15

L'une des différences fondamentales entre cmdles shells Windows et POSIX est de savoir qui est responsable des extensions génériques. Les shells font toutes les extensions nécessaires avant de démarrer les commandes réelles que vous avez demandées. transmet cmd principalement les motifs génériques aux commandes non modifiées. (Je dis surtout, car je pense qu'il y a des exceptions, et les variables d'environnement sont développées dans la plupart des circonstances.) Cela rend l'écriture renamequi fonctionnerait avec la même syntaxe que dans cmdassez délicate.

Mais il y en a renamepour Linux - avec des arguments complètement différents, consultez la page de manuel (qui est un peu laconique sur mon système, et renamevient du util-linuxpackage sur mon système, qui devrait être largement disponible). Votre premier changement de nom se ferait comme ceci:

rename .txt .bak *.txt

Notez que le shell fait l' *expansion, donc renamelui-même pense qu'il a été invoqué comme ceci:

rename .txt .bak file1.txt file2.txt file3.txt ...

Vous pouvez donc deviner la version à fichier unique:

rename .txt .bak file1.txt

Si vous ne voulez pas l'utiliser renamemais l'implémenter vous-même, vous pouvez créer une fonction pour cela. En supposant que vous ne souhaitiez modifier que l'extension du fichier et pour renommer un seul fichier, regardez ceci:

$ function chext() {
  newext="$1"
  file="$2"
  newfile="${file%.*}$newext"
  echo mv "$file" "$newfile"
}
$ chext .csv test.txt
mv text.txt text.csv

$newfileest construit à l'aide d'une suppression de sous - chaîne pour supprimer l'extension d'origine, puis concatène la nouvelle extension. Vous pouvez étendre cette fonction pour gérer plusieurs fichiers relativement facilement.


Quant à votre lsquestion, utilisez l' -dinterrupteur. Cela empêchera lsde lister le contenu des répertoires.

Démo:

$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:29 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.csv
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

Renommage générique

$ rename .csv .txt f*
$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:34 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.txt
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

Renommer un fichier

$ rename .txt .csv f1.txt 
$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:34 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.txt
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

Le défaut ls

$ ls -l t*
-rw-r--r-- 1 owner users    0 Jan  7 17:27 test.csv

test:
total 0
-rw-r--r-- 1 owner users 0 Jan  7 17:33 dont_show_me_please

ls qui n'inspecte pas les répertoires

$ ls -ld t*
drwxr-xr-x 2 owner users 4096 Jan  7 17:33 test
-rw-r--r-- 1 owner users    0 Jan  7 17:27 test.csv
Tapis
la source
très agréable! je pense que je viens de monter d'une ceinture avec mon linux-fu! Merci!
cwd
Astuce pour progresser un peu avec cela (et éviter les écueils): utilisez des fonctions ou des scripts simples pour voir ce qui se passe lorsque somefunc *.Extce modèle ne correspond à aucun fichier, et jouez avec des guillemets pour voir si vous parvenez à transmettre un modèle ( sans l'avoir développé) entre les fonctions. Conseil de sécurité: ne pas utiliser rmn'importe où lors de l'expérimentation de l'expansion globulaire / shell :-)
Mat
4

Une chose à garder à l'esprit en ce qui concerne les caractères génériques est qu'ils sont développés par le shell. L'application ne sait pas si vous avez utilisé des caractères génériques ou tapé les noms. Par exemple, si vous tapez rename *.txt *.bak, la renamecommande voit quelque chose comme rename file1.txt file2.txt existingfile.bak. Ce n'est pas assez d'informations pour continuer.

Je vais d'abord traiter de la question ls, car c'est plus simple. Si tout ce que vous voulez, ce sont les noms correspondants, alors vous n'en avez pas besoin ls, car le shell fait déjà l'expansion.

echo t*

Si vous voulez plus d'informations sur les fichiers, passez l' -doption à ls, pour lui dire de ne pas lister le contenu des répertoires.

ls -ld t*

Il n'y a pas d'utilitaire standard pour renommer les fichiers, car les premiers systèmes Unix n'en étaient pas livrés. La méthode portable pour renommer des fichiers utilise une boucle et est un peu détaillée:

for x in *.txt; do mv -- "$x" "${x%.txt}.bak"; done

Il existe plusieurs utilitaires courants pour renommer des fichiers, dont aucun n'est garanti d'être installé sur un système Unix donné, mais tous sont faciles à installer. En voici les principaux:

  • renamedepuis la util-linuxsuite, disponible sur tous les systèmes Linux non intégrés (et nulle part ailleurs). Sur Debian et ses dérivés (y compris Ubuntu), cette commande est appelée rename.ul. À condition qu'il n'y ait pas d' .txtautre occurrence que l'extension finale, vous pouvez écrire

    rename .txt .bak *.txt
    
  • renameest un script Perl fourni par Debian et ses dérivés /usr/bin/rename. Vous pouvez renommer des fichiers selon des commandes Perl arbitraires.

    rename 's/\.txt\z/\.bak/' *.txt
    
  • mmv, qui peut renommer, copier et lier des fichiers selon plusieurs modèles basés sur le nom et propose de nombreuses options concernant ce qui se passe si un nom cible existe déjà. Notez que vous devez utiliser des guillemets pour protéger les caractères génériques contre l'expansion par le shell.

    mmv '*.txt' '#1.txt'
    
  • zmvest une fonction zsh , disponible si et seulement si votre shell est zsh. Il peut correspondre à des modèles zsh arbitraires (vous pouvez donc faire correspondre les noms de fichiers en fonction d'expressions régulières arbitraires, pas seulement des caractères génériques, et vous pouvez faire correspondre les fichiers selon d'autres critères tels que les dates et les tailles). zmvpeut également copier et lier.

    zmv '(*).txt' '$1.txt'
    

Si vous avez un certain contrôle sur les machines que vous utilisez, ma recommandation est d'utiliser zsh comme shell (il a d'autres avantages par rapport à bash) et de mettre ces lignes dans votre ~/.zshrc:

autoload -U zmv
alias zmv='noglob zmv -w'
alias zcp='zmv -C'
alias zln='zmv -L'
alias zsy='zmv -Ls'

noglobest une fonctionnalité zsh qui indique au shell de ne pas développer les caractères génériques dans l'argument de la commande. De cette façon, vous pouvez écrire zmv *.txt \$1.txt(vous aurez toujours besoin de protéger le $dans un texte de remplacement).

Gilles 'SO- arrête d'être méchant'
la source