1
votes

Quelle est la meilleure façon de faire cette transformation de chaîne dans le shell?

J'essaie d'écrire un script shell qui devra transformer l'entrée de la forme suivante:

lines.each { |s| s.split('/')[2..-1].join('-').split('.')[0] }

En:

cut -d '/' -f x

C'est à dire diviser sur '/', supprimer les 2 premiers segments, supprimer le '.txt' et remplacer les barres obliques restantes par des tirets.

La substitution semble assez simple en utilisant tr:

paths=$(cat <<- EOF
foo/bar/baz/qux.txt
bar/baz/quz.txt
baz/quz/foo.txt
EOF
)

echo $paths | tr '/' '-' | tr '.txt' ' '


1 commentaires

La commande tr '.txt' '' ne fait pas ce que vous voulez - elle ne supprime pas le ".txt", elle le convertit en 4 espaces, et il fait également la même chose avec tout autre "t", "x" et "." dans le nom. Par exemple, "test1.txt" -> "es 1".


6 Réponses :


6
votes

Cela peut être fait en utilisant bash extensions de paramètres :

baz-qux
quz
foo

Donne:

for name in foo/bar/baz/qux.txt bar/baz/quz.txt baz/quz/foo.txt; do
    new=${name#*/}   # drop the shortest prefix match for */, thus everything up to first /
    new=${new#*/}    # repeat, dropping the second segment
    new=${new%.txt}  # drop shortest suffix match for .txt
    new=${new//\//-} # convert any remaining slashes
    echo "$new"
done

Ce sont toutes des constructions intégrées du shell bash , donc pas de processus externes comme cut , sed ou tr requis.


6 commentaires

Accepteriez-vous une modification déplaçant les commentaires vers un formulaire en ligne, pour éviter que le code ne paraisse plus long qu’il ne l’est?


Bien sûr @CharlesDuffy. J'avais envisagé de mettre la viande dans une fonction, ce que je ferais probablement si le code était pour moi.


Compte tenu de la première ligne de l'exemple d'entrée, je pense que new = $ {name ## * /}; new = $ {new% .txt} serait tout ce qui est nécessaire? Bonne chance à tous.


Se sentir dense - ne pas voir où le dernier / restant est changé en - .


@PaulHodges le gars qui a édité a probablement manqué cette ligne: new = $ {new // \ // -} Je l'ai copiée pour un test local dans la première version de l'article


Y a-t-il une raison de ne pas utiliser new = $ {name # * / * /} au lieu de deux extensions?



1
votes

Vous pouvez essayer quelque chose comme ceci: cut -d / -f3- | couper -d. -f1 | tr / -

Explication:

  • cut -d / -f3- - fractionné sur '/', et conserver le troisième champ et tout ce qui suit ( baz / qux.txt )
  • coupe -d. -f1 - fractionné sur '.', conserve la première valeur (supprime l'extension de fichier) ( baz / qux )
  • tr / - - Transforme tout "/" restant en "-". ( baz-qux )

1 commentaires

Conceptuellement simple, mais la mise en place de trois processus distincts représente beaucoup de frais généraux.



1
votes

Vous pouvez tout faire en une seule commande sed

sed -E 's|([^/]*/){,2}||; s|/|-|g; s|\.txt$||' file

Remplacez \ .txt $ par \. [^. ] $ pour supprimer toutes les extensions au lieu de seulement .txt.


1 commentaires

Ce sera la meilleure approche si le traitement d'un grand nombre d'entrées en une seule exécution (suffisamment pour surmonter les coûts de démarrage en temps constant de sed avec ses E / S plus rapides une fois en cours d'exécution), ou cdarke si c'est un nombre plus petit.



1
votes

Essayez Perl

$ cat mark_smith.txt
foo/bar/baz/qux.txt
bar/baz/quz.txt
baz/quz/foo.txt

$ perl -F"/" -lane ' @a=@F[2..$#F]; @b=map{s/.txt//g;$_} @a; print join("-",@b) ' mark_smith.txt
baz-qux
quz
foo

$


0 commentaires

1
votes

en supposant que . est uniquement dans les noms de fichiers

$ awk -F[/.] '{n=NF; p=$(n-1)} n>4{p=$(n-2)"-"p} {print p}' file

baz-qux
quz
foo


0 commentaires

1
votes
awk '{gsub(/^.{8}|.txt$/,"")sub(/\//,"-")}1' file

baz-qux
quz
foo

0 commentaires