Douzaine d'astuces avec le shell Linux qui pourraient vous faire gagner du temps



  • Tout d'abord, vous pouvez lire cet article en russe ici .

Un soir, je lisais les expressions régulières Mastering de Jeffrey Friedl , j'ai réalisé que même si vous avez toute la documentation et beaucoup d'expérience, il pourrait y avoir beaucoup de trucs développés par différentes personnes et emprisonnés pour eux-mêmes. Tout le monde est différent. Et les techniques qui sont évidentes pour certaines personnes peuvent ne pas être évidentes pour d'autres et ressembler à une sorte de magie étrange pour une troisième personne. Soit dit en passant, j'ai déjà décrit plusieurs de ces moments ici (en russe) .

Pour l'administrateur ou l'utilisateur, la ligne de commande n'est pas seulement un outil qui peut tout faire, mais aussi un outil hautement personnalisé qui pourrait être développé à jamais. Récemment, il y a eu un article traduit sur quelques astuces utiles dans CLI. Mais je pense que le traducteur n'a pas assez d'expérience avec CLI et n'a pas suivi les astuces décrites, donc beaucoup de choses importantes pourraient être manquées ou mal comprises.

Under the cut - une douzaine de trucs dans le shell Linux à partir de mon expérience personnelle.

Remarque: Tous les scripts et exemples de l'article ont été spécialement simplifiés autant que possible - donc peut-être que vous pouvez trouver plusieurs astuces qui semblent complètement inutiles - c'est peut-être la raison. Mais en tout cas, partagez votre esprit dans les commentaires!

1. Fractionner la chaîne avec des extensions variables


Les gens utilisent souvent couper ou même awk juste pour soustraire une partie de la chaîne par motif ou avec des séparateurs.
De plus, beaucoup de gens utilisent l'opération bash de sous-chaîne en utilisant $ {VARIABLE: start_position: length}, qui fonctionne très rapidement.

Mais bash fournit un moyen puissant de manipuler avec des chaînes de texte en utilisant #, ##,% et %% - il a appelé les extensions de variable bash .
En utilisant cette syntaxe, vous pouvez couper le nécessaire par le modèle sans exécuter de commandes externes, donc cela fonctionnera très rapidement.

L'exemple ci-dessous montre comment obtenir la troisième colonne (shell) de la chaîne où les valeurs séparées par deux points "username: homedir: shell" en utilisant cut ou en utilisant des extensions de variable (nous utilisons le masque *: et la commande ##, ce qui signifie: cut tous les caractères à gauche jusqu'au dernier deux-points trouvé):

$ STRING="username:homedir:shell" $ echo "$STRING"|cut -d ":" -f 3 shell $ echo "${STRING##*:}" shell 

La deuxième option ne démarre pas le processus enfant ( couper ) et n'utilise pas du tout de tuyaux, ce qui devrait fonctionner beaucoup plus rapidement. Et si vous utilisez le sous-système bash sur les fenêtres, où les tuyaux bougent à peine, la différence de vitesse sera importante .

Voyons un exemple sur Ubuntu - exécutons notre commande en boucle 1000 fois

 $ cat test.sh #!/usr/bin/env bash STRING="Name:Date:Shell" echo "using cut" time for A in {1..1000} do cut -d ":" -f 3 > /dev/null <<<"$STRING" done echo "using ##" time for A in {1..1000} do echo "${STRING##*:}" > /dev/null done 

Résultats
 $ ./test.sh using cut real 0m0.950s user 0m0.012s sys 0m0.232s using ## real 0m0.011s user 0m0.008s sys 0m0.004s 

La différence est plusieurs dizaines de fois!

Bien sûr, l'exemple ci-dessus est trop artificiel. Dans un exemple réel, nous ne travaillerons pas avec une chaîne statique, nous voulons lire un vrai fichier. Et pour la commande ' cut ', nous redirigeons simplement / etc / passwd vers elle. Dans le cas de ##, nous devons créer une boucle et lire le fichier en utilisant la commande interne ' read '. Alors, qui gagnera cette affaire?

 $ cat test.sh #!/usr/bin/env bash echo "using cut" time for count in {1..1000} do cut -d ":" -f 7 </etc/passwd > /dev/null done echo "using ##" time for count in {1..1000} do while read do echo "${REPLY##*:}" > /dev/null done </etc/passwd done 
Résultat
 $ ./test.sh $ ./test.sh using cut real 0m0.827s user 0m0.004s sys 0m0.208s using ## real 0m0.613s user 0m0.436s sys 0m0.172s 
Pas de commentaires =)

Quelques exemples supplémentaires:

Extrayez la valeur après un caractère égal:

 $ VAR="myClassName = helloClass" $ echo ${VAR##*= } helloClass 

Extraire le texte entre crochets:

 $ VAR="Hello my friend (enemy)" $ TEMP="${VAR##*\(}" $ echo "${TEMP%\)}" enemy 

2. Bash autocompletion with tab


Le paquet bash-complétement fait partie de presque toutes les distributions Linux. Vous pouvez l'activer dans /etc/bash.bashrc ou /etc/profile.d/bash_completion.sh, mais il est généralement déjà activé par défaut. En général, la saisie semi-automatique est l'un des premiers moments pratiques sur le shell Linux que rencontre tout d'abord un nouveau venu.

Mais le fait que tout le monde n'utilise pas toutes les fonctionnalités de complétion de bash, et à mon avis, est complètement vain. Par exemple, tout le monde ne sait pas que la saisie semi-automatique fonctionne non seulement avec les noms de fichiers, mais aussi avec les alias, les noms de variables, les noms de fonctions et pour certaines commandes, même avec des arguments. Si vous creusez dans des scripts de saisie semi-automatique, qui sont en fait des scripts shell, vous pouvez même ajouter la saisie semi-automatique pour votre propre application ou script.
Mais revenons aux alias.

Vous n'avez pas besoin de modifier la variable PATH ou de créer des fichiers dans le répertoire spécifié pour exécuter l'alias. Il vous suffit de les ajouter au profil ou au script de démarrage et de les exécuter de n'importe où.

Habituellement, nous utilisons des lettres minuscules pour les fichiers et répertoires dans * nix, il peut donc être très confortable de créer des alias majuscules - dans ce cas, bash-complétera devinera votre commande presque avec une seule lettre:

 $ alias TAsteriskLog="tail -f /var/log/asteriks.log" $ alias TMailLog="tail -f /var/log/mail.log" $ TA[tab]steriksLog $ TM[tab]ailLog 

3. Bash autocompletion with tab - partie 2


Pour les cas plus compliqués, vous aimeriez probablement mettre vos scripts personnels dans $ HOME / bin.
Mais nous avons des fonctions dans bash.

Les fonctions ne nécessitent ni chemin ni fichiers séparés. Et (attention) l'achèvement de bash fonctionne aussi avec les fonctions.

Créons la fonction LastLogin dans .profile (n'oubliez pas de recharger .profile):

 function LastLogin { STRING=$(last | head -n 1 | tr -s " " " ") USER=$(echo "$STRING"|cut -d " " -f 1) IP=$(echo "$STRING"|cut -d " " -f 3) SHELL=$( grep "$USER" /etc/passwd | cut -d ":" -f 7) echo "User: $USER, IP: $IP, SHELL=$SHELL" } 

(En fait, il n'y a pas d'importance à ce que fait cette fonction, c'est juste un exemple de script que nous pouvons mettre dans le script séparé ou même dans l'alias, mais la fonction pourrait être meilleure) .

Dans la console (veuillez noter que le nom de la fonction a une première lettre majuscule pour accélérer l'accomplissement de bash):

 $ L[tab]astLogin User: saboteur, IP: 10.0.2.2, SHELL=/bin/bash 

4.1. Données sensibles


Si vous mettez de l'espace avant toute commande dans la console, il n'apparaîtra pas dans l'historique des commandes, donc si vous avez besoin de mettre un mot de passe en texte brut dans la commande, c'est une bonne façon d'utiliser cette fonctionnalité - regardez l'exemple ci-dessous, echo "hello 2 " n'apparaîtra pas dans l'historique:

 $ echo "hello" hello $ history 2 2011 echo "hello" 2012 history 2 $ echo "my password secretmegakey" # there are two spaces before 'echo' my password secretmegakey $ history 2 2011 echo "hello" 2012 history 2 

C'est facultatif
Il est généralement activé par défaut, mais vous pouvez configurer ce comportement dans la variable suivante:

export HISTCONTROL = ignoreboth


4.2. Données sensibles dans les arguments de ligne de commande


Vous souhaitez stocker certains scripts shell dans git pour les partager entre les serveurs, ou cela peut faire partie du script de démarrage de l'application. Et vous voulez que ce script se connecte à la base de données ou fasse toute autre chose qui nécessite des informations d'identification.

Bien sûr, c'est une mauvaise idée de stocker les informations d'identification dans le script lui-même, car git n'est pas sécurisé.

Habituellement, vous pouvez utiliser des variables, qui étaient déjà définies dans les environnements cibles, et votre script ne contiendra pas les mots de passe lui-même.

Par exemple, vous pouvez créer un petit script sur chaque environnement avec 700 autorisations et l'appeler à l'aide de la commande source du script principal:

 secret.sh PASSWORD=LOVESEXGOD 

 myapp.sh source ~/secret.sh sqlplus -l user/"$PASSWORD"@database:port/sid @mysqfile.sql 

Mais ce n'est pas sûr.

Si quelqu'un d'autre peut se connecter à votre hôte, il peut simplement exécuter la commande ps et voir votre processus sqlplus avec tous les arguments de la ligne de commande, y compris les mots de passe. Ainsi, les outils sécurisés devraient généralement pouvoir lire les mots de passe / clés / données sensibles directement à partir des fichiers.

Par exemple - ssh sécurisé n'a même aucune option pour fournir un mot de passe en ligne de commande. Mais il peut lire la clé ssh à partir du fichier (et vous pouvez définir des autorisations sécurisées sur le fichier de clé ssh).

Et les wgets non sécurisés ont une option "--password" qui vous permet de fournir un mot de passe en ligne de commande. Et tout le temps que wget fonctionnera, tout le monde peut exécuter la commande ps et voir le mot de passe que vous avez fourni.

De plus, si vous avez beaucoup de données sensibles et que vous souhaitez les contrôler depuis git, le seul moyen est le chiffrement. Ainsi, vous ne mettez dans chaque environnement cible que le mot de passe principal et toutes les autres données que vous pouvez chiffrer et mettre à git. Et vous pouvez travailler avec des données chiffrées à partir de la ligne de commande, en utilisant l'interface CLI openssl. Voici un exemple pour chiffrer et déchiffrer à partir de la ligne de commande:

Le fichier secret.key contient la clé principale - une seule ligne:

 $ echo "secretpassword" > secret.key; chmod 600 secret.key 

Permet d'utiliser aes-256-cbc pour crypter une chaîne:

 $ echo "string_to_encrypt" | openssl enc -pass file:secret.key -e -aes-256-cbc -a U2FsdGVkX194R0GmFKCL/krYCugS655yLhf8aQyKNcUnBs30AE5lHN5MXPjjSFML 

Vous pouvez mettre cette chaîne cryptée dans n'importe quel fichier de configuration stocké dans git, ou n'importe quel autre endroit - sans secret.key, il est presque impossible de le décrypter.
Pour décrypter exécuter la même commande, il suffit de remplacer -e par -d:

 $ echo 'U2FsdGVkX194R0GmFKCL/krYCugS655yLhf8aQyKNcUnBs30AE5lHN5MXPjjSFML' | openssl enc -pass file:secret.key -d -aes-256-cbc -a string_to_encrypt 

5. La commande grep


Tout le monde devrait connaître la commande grep. Et soyez amical avec les expressions régulières. Et souvent, vous pouvez écrire quelque chose comme:

 tail -f application.log | grep -i error 

Ou même comme ça:

 tail -f application.log | grep -i -P "(error|warning|failure)" 

Mais n'oubliez pas que grep a beaucoup d'options merveilleuses. Par exemple -v, qui annule votre recherche et affiche tous les messages sauf "info":

 tail -f application.log | grep -v -i "info" 

Trucs supplémentaires:

L'option -P est très utile, car par défaut grep utilise une "expression régulière de base:" assez obsolète, et -P active PCRE qui ne connaît même pas le regroupement.
-i ignore la casse.
--line-buffered analyse la ligne immédiatement au lieu d'attendre d'atteindre le tampon standard de 4k (utile pour tail -f | grep).

Si vous connaissez bien l'expression régulière, avec --only-matching / -o vous pouvez vraiment faire de grandes choses en coupant du texte. Il suffit de comparer les deux commandes suivantes pour extraire le shell de myuser:

 $ grep myuser /etc/passwd| cut -d ":" -f 7 $ grep -Po "^myuser(:.*){5}:\K.*" /etc/passwd 

La deuxième commande semble plus compilée, mais elle n'exécute que grep au lieu de grep et cut , donc son exécution prendra moins de temps.

6. Comment réduire la taille du fichier journal


Dans * nix, si vous supprimez le fichier journal, qui est actuellement utilisé par une application, vous ne pouvez pas simplement supprimer tous les journaux, vous pouvez empêcher l'application d'écrire de nouveaux journaux jusqu'au redémarrage.

Parce que le descripteur de fichier n'ouvre pas le nom du fichier, mais la structure iNode, et l'application continuera à écrire dans le descripteur de fichier dans le fichier, qui n'a pas d'entrée de répertoire, et ce fichier sera supprimé automatiquement après l'arrêt de l'application par le système de fichiers ( votre application peut ouvrir et fermer le fichier journal à chaque fois qu'il souhaite écrire quelque chose pour éviter un tel problème, mais cela affecte les performances ).

Alors, comment effacer le fichier journal sans le supprimer:

 echo "" > application.log 

Ou nous pouvons utiliser la commande tronquer:

 truncate --size=1M application.log 

Mentionnez, cette commande tronquer supprimera le reste du fichier, vous perdrez donc les derniers événements du journal. Vérifiez un autre exemple comment stocker les 1000 dernières lignes:

 echo "$(tail -n 1000 application.log)" > application.log 

PS Sous Linux, nous avons un journal de service standard. Vous pouvez ajouter vos journaux à la troncature / rotation automatisée ou utiliser les bibliothèques de journaux existantes qui peuvent le faire pour vous (comme log4j en java).

7. Watch vous attend!


Il y a une situation où vous attendez la fin d'un événement. Par exemple, pendant qu'un autre utilisateur se connecte au shell (vous exécutez continuellement la commande who ), ou que quelqu'un doit copier le fichier sur votre machine à l'aide de scp ou ftp et que vous attendez l'achèvement (en répétant ls des dizaines de fois).

Dans de tels cas, vous pouvez utiliser

 watch <command> 

Par défaut, sera exécuté toutes les 2 secondes avec pré-effacement de l'écran jusqu'à ce que Ctrl + C soit pressé. Vous pouvez configurer la fréquence d'exécution.

Il est très utile lorsque vous souhaitez consulter des journaux en direct.

8. Séquence de coup


Il existe une construction très utile pour créer des plages. Par exemple, au lieu de quelque chose comme ça:

 for srv in 1 2 3 4 5; do echo "server${srv}";done server1 server2 server3 server4 server5 

Vous pouvez écrire ce qui suit:

 for srv in server{1..5}; do echo "$srv";done server1 server2 server3 server4 server5 

Vous pouvez également utiliser la commande seq pour générer des plages formatées. Par exemple, nous pouvons utiliser seq pour créer des valeurs qui seront automatiquement ajustées en largeur (00, 01 au lieu de 0, 1):

 for srv in $(seq -w 5 10); do echo "server${srv}";done server05 server06 server07 server08 server09 server10 

Un autre exemple avec la substitution de commandes - renommer des fichiers. Pour obtenir le nom de fichier sans extension, nous utilisons la commande ' basename ':

 for file in *.txt; do name=$(basename "$file" .txt);mv $name{.txt,.lst}; done 

Encore plus court avec '%':

 for file in *.txt; do mv ${file%.txt}{.txt,.lst}; done 

PS En fait, pour renommer les fichiers, vous pouvez essayer l'outil « renommer » qui a beaucoup d'options.

Un autre exemple - permet de créer une structure pour un nouveau projet java:

 mkdir -p project/src/{main,test}/{java,resources} 

Résultat
 project/ !--- src/ |--- main/ | |-- java/ | !-- resources/ !--- test/ |-- java/ !-- resources/ 

9. queue, plusieurs fichiers, plusieurs utilisateurs ...


J'ai mentionné le multitail pour lire des fichiers et regarder plusieurs journaux en direct. Mais il n'est pas fourni par défaut, et les autorisations pour installer quelque chose ne sont pas toujours disponibles.

Mais la queue standard peut aussi le faire:

 tail -f /var/logs/*.log 

Permet également de se souvenir des utilisateurs, qui utilisent des alias «tail -f» pour surveiller les journaux des applications.
Plusieurs utilisateurs peuvent regarder les fichiers journaux simultanément en utilisant «tail -f». Certains d'entre eux ne sont pas très précis avec leurs sessions. Ils pourraient laisser «queue -f» en arrière-plan pour une raison quelconque et l'oublier.

Si l'application a été redémarrée, il existe ces processus «tail -f» en cours d'exécution qui surveillent un fichier journal inexistant qui peut se bloquer pendant plusieurs jours, voire plusieurs mois.

Habituellement, ce n'est pas un gros problème, mais pas proprement.

Dans le cas, si vous utilisez un alias pour regarder le journal, vous pouvez modifier cet alias avec l'option --pid:

 alias TFapplog='tail -f --pid=$(cat /opt/app/tmp/app.pid) /opt/app/logs/app.log' 

Dans ce cas, toutes les queues seront automatiquement terminées lorsque l'application cible sera redémarrée.

10. Créer un fichier avec une taille spécifiée


dd était l'un des outils les plus populaires pour travailler avec des données de blocs et binaires. Par exemple, créer un fichier de 1 Mo rempli de zéro sera:

 dd if=/dev/zero of=out.txt bs=1M count=10 

Mais je recommande d'utiliser fallocate :

 fallocate -l 10M file.txt 

Sur les systèmes de fichiers, qui prennent en charge la fonction d'allocation (xfs, ext4, Btrfs ...), fallocate sera exécuté instantanément, contrairement à l'outil dd. De plus, allouer signifie une allocation réelle de blocs, pas la création d'un fichier de rechange.

11. xargs


Beaucoup de gens connaissent la commande xargs . Mais tous n'utilisent pas les deux options suivantes, ce qui pourrait grandement améliorer votre script.

Premièrement - vous pouvez obtenir une très longue liste d'arguments à traiter, et cela pourrait dépasser la longueur de la ligne de commande (par défaut ~ 4 ko).

Mais vous pouvez limiter l'exécution en utilisant l'option -n, donc xargs exécutera la commande plusieurs fois, en envoyant un nombre spécifié d'arguments à la fois:

 $ # lets print 5 arguments and send them to echo with xargs: $ echo 1 2 3 4 5 | xargs echo 1 2 3 4 5 $ # now let's repeat, but limit argument processing by 3 per execution $ echo 1 2 3 4 5 | xargs -n 3 echo 1 2 3 4 5 

Allez-y. Le traitement d'une longue liste peut prendre beaucoup de temps, car il s'exécute dans un seul thread. Mais si nous avons plusieurs cœurs, nous pouvons dire à xargs de fonctionner en parallèle:

 echo 1 2 3 4 5 6 7 8 9 10| xargs -n 2 -P 3 echo 

Dans l'exemple ci-dessus, nous demandons à xargs de traiter la liste en 3 threads; chaque thread prendra et traitera 2 arguments par exécution. Si vous ne savez pas combien de cœurs vous avez, optimisons ceci en utilisant " nproc ":

 echo 1 2 3 4 5 6 7 8 9 10 | xargs -n 2 -P $(nproc) echo 

12. dormir? tout? lisez!


Un certain temps, vous devez attendre plusieurs secondes. Ou attendez l'entrée utilisateur avec lecture:

 read -p "Press any key to continue " -n 1 

Mais vous pouvez simplement ajouter une option de délai d'attente à la commande de lecture , et votre script sera mis en pause pendant un nombre spécifié de secondes, mais en cas d'exécution interactive, l'utilisateur peut facilement ignorer l'attente.

 read -p "Press any key to continue (auto continue in 30 seconds) " -t 30 -n 1 

Vous pouvez donc oublier la commande sleep.

Je soupçonne que toutes mes astuces ne semblent pas intéressantes, mais il me semble qu'une douzaine sont un bon nombre à remplir.

Sur ce, je dis au revoir et je serai reconnaissant d'avoir participé à l'enquête.

Bien sûr, n'hésitez pas à discuter de ce qui précède et à partager vos trucs sympas dans les commentaires!

Source: https://habr.com/ru/post/fr444890/


All Articles