Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Script dos2unix ultra portable

23 réponses
Avatar
Stephane Gibier
Salut,

Je souhaite intégrer dans un script d'installation (install.sh) une
méthode le plus portable possible pour transformer des fichiers *.ini du
format 'DOS' (CR/LF) au format 'Unix' (LF). Pour le moment, j'utilise le
bout de script suivant qui fonctionne très bien sous Linux :

find . -name '*.ini' -exec sh -c 'sed 's/.$//' <{} >{}~; mv {}~ {}' \;

Cela ne fonctionne pas sous AIX 4.3 car le 'joker' {} n'est pas
substitué dans ce cas là. Je recherche donc un moyen hyper portable pour
effectuer cette bête transformation, sans utiliser perl, avec les moyens
posix du bord (find, sed, sh)

Auriez vous une idée ?

Merci

--
Stéphane Gibier <stephane@gibier.org>
Le maniement réitéré des fonds multiplie les mouvements de caisse
Yr znavrzrag eévgéeé qrf pbaf zhygvcyvr yrf zbhirzragf qr srffr

10 réponses

1 2 3
Avatar
Pascal Bourguignon
Laurent Wacrenier <lwa@ teaser . fr> writes:

Pascal Bourguignon écrit:

Ici, avec cp, les droits et propriétaire et groupe des fichiers ont
été conservé. Magie?


Pas le bit 's' normalement.


Mais normalement les bits 's' ne sont pas à 1.



--
__Pascal_Bourguignon__
http://www.informatimago.com/
Do not adjust your mind, there is a fault in reality.


Avatar
Stephane Gibier

Certainement pas, "tr" existait dans les toutes premières
versions d'Unix bien avant sed et est aujourd'hui tout aussi
standard que sed.


Ok, je le note...

<troll>
Le problème (!) avec Linux, c'est qu'on a l'impression que tous les Unix
sont identiques :-) Et quand on tombe au boulot avec un AIX sans pages
de man, galère. Et si c'est chez un client...
</troll>

find . -name '*.ini' -type f -exec sh -c '
cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1"' {} {} ;


Elégant ! Mais je ne comprend pas pourquoi {} {}. Un seul suffit pour
qu'il soit substitué en $1 dans le subshell non ?


l'intéret de le faire dans ce sens avec cp est qu'on conserve
les permission et l'i-node du fichier original.


Exact.

--
Stéphane Gibier
Le maniement réitéré des fonds multiplie les mouvements de caisse
Yr znavrzrag eévgéeé qrf pbaf zhygvcyvr yrf zbhirzragf qr srffr

Avatar
Stephane Gibier

find . -name '*.ini' -print
| while read file ; do
cp "$file" "$file"~
&& sed 's/.$//' < "$file"~ > "$file" # ou tr si on préfère
done


Génial ! J'avais complètement oublié le coup du "while read" ! Pas de
subshell, pipé et ultra portable.

Merci !

Noter l'usage de cp à la place de mv qui permet de conserver les
droits d'accès et propriétaire et groupe des fichiers.


Noté.

--
Stéphane Gibier
Cette femme est folle de la messe
Prggr srzzr rfg zbyyr qr yn srffr

Avatar
Stephane CHAZELAS
Le 7 Oct 2003 19:56:52 GMT, Stephane Gibier écrivait :
[...]
find . -name '*.ini' -type f -exec sh -c '
cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1"' {} {} ;


Elégant ! Mais je ne comprend pas pourquoi {} {}. Un seul suffit pour
qu'il soit substitué en $1 dans le subshell non ?
[...]


Suivant le sh

sh -c 'echo "$0" "$1"' A B

donnera "sh A" ou "A B"


sh -c 'echo "$1"' A A

donnera A pour les deux types de sh.

--
Stéphane


Avatar
Stephane CHAZELAS
Le 7 Oct 2003 19:58:53 GMT, Stephane Gibier écrivait :

find . -name '*.ini' -print
| while read file ; do
cp "$file" "$file"~
&& sed 's/.$//' < "$file"~ > "$file" # ou tr si on préfère
done


Génial ! J'avais complètement oublié le coup du "while read" ! Pas de
subshell, pipé et ultra portable.
[...]


Note qu'il faut remplacer "read file" par "IFS= read -r file" ou
tu auras des soucis avec les noms de fichiers contenant dans
backslashes ou se finissant par des caractères blancs. Et comme
l'a dit Pascal, tu auras toujours des problèmes avec les noms de
fichiers contenant des NL.

"read -r" n'est pas des plus portables en plus. Donc, faudrait
faire:

find ... | sed 's//&&/g' | while IFS= read file;....

Vaut mieux utiliser le -exec sh -c ...


--
Stéphane


Avatar
Pascal Bourguignon
Stephane Gibier writes:


Certainement pas, "tr" existait dans les toutes premières
versions d'Unix bien avant sed et est aujourd'hui tout aussi
standard que sed.


Ok, je le note...

<troll>
Le problème (!) avec Linux, c'est qu'on a l'impression que tous les Unix
sont identiques :-) Et quand on tombe au boulot avec un AIX sans pages
de man, galère. Et si c'est chez un client...
</troll>

find . -name '*.ini' -type f -exec sh -c '
cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1"' {} {} ;


Elégant ! Mais je ne comprend pas pourquoi {} {}. Un seul suffit pour
qu'il soit substitué en $1 dans le subshell non ?


Mais j'ai l'impression qu'on y forke plus qu'avec ma solution:

find . -name *.init -type f -print
| while read f ; do cp $f $f~ && tr -d 15 < $f~ > $f ; done

Quand le while (ou autre structure de contrôle shell) est en dernière
position d'un pipe, le shell exécute lui même cette structure de
contrôle au lieu de forker un sous-shell.


--
__Pascal_Bourguignon__
http://www.informatimago.com/
Do not adjust your mind, there is a fault in reality.


Avatar
Stephane CHAZELAS
Le 07 Oct 2003 20:28:55 GMT, Stephane CHAZELAS écrivait :
[...]
Vaut mieux utiliser le -exec sh -c ...


Et note, que pour etre pédant et minimiser le nombre de
commandes lancées tout en étant portable, on peut faire:

find ./. ( -name '*.ini' -o -name '.*.ini' -o -name .ini )
-type f -print | sed 's/./&/g' | awk '
{
if (NR > 1) {
printf("%s", line)
if ($0 !~ //.//)
printf("")
print ""
}
line = $0
}
END {
print line
}' | xargs sh -c 'shift "$1"
for file
do cp "$file" "${file}~" && tr -d 15 < "${file}~" > "$file"
done' 2 1

C'qu'on se marre avec le shell quand-meme !

--
Stéphane

Avatar
Stephane CHAZELAS
Le 07 Oct 2003 22:59:30 +0200, Pascal Bourguignon écrivait :
[...]
find . -name '*.ini' -type f -exec sh -c '
cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1"' {} {} ;
[...]



Mais j'ai l'impression qu'on y forke plus qu'avec ma solution:

find . -name *.init -type f -print
| while read f ; do cp $f $f~ && tr -d 15 < $f~ > $f ; done


On n'y forke pas plus (1 + 2n dans les deux cas voire 2 + 2n
pour la tienne, faudra peut-etre remplacer "tr" par "exec tr"
avec la mienne pour éviter le 1 + 3n avec certains sh), mais on
lance plus de commandes (1 shell par fichier).

Quand le while (ou autre structure de contrôle shell) est en dernière
position d'un pipe, le shell exécute lui même cette structure de
contrôle au lieu de forker un sous-shell.


Ça dépend des shells. bash/pdksh s'opposent à zsh/ksh là dessus.

--
Stéphane



Avatar
Pascal Bourguignon
Stephane CHAZELAS writes:

Le 07 Oct 2003 20:28:55 GMT, Stephane CHAZELAS écrivait :
[...]
Vaut mieux utiliser le -exec sh -c ...


Et note, que pour etre pédant et minimiser le nombre de
commandes lancées tout en étant portable, on peut faire:

find ./. ( -name '*.ini' -o -name '.*.ini' -o -name .ini )
-type f -print | sed 's/./&/g' | awk '
{
if (NR > 1) {
printf("%s", line)
if ($0 !~ //.//)
printf("")
print ""
}
line = $0
}
END {
print line
}' | xargs sh -c 'shift "$1"
for file
do cp "$file" "${file}~" && tr -d 15 < "${file}~" > "$file"
done' 2 1

C'qu'on se marre avec le shell quand-meme !


Oulala! Mais il y a des sacrés différences avec les awk, gawk, nawk...
Je me demande si c'est pas pire que les sh A B ou sh A A...

--
__Pascal_Bourguignon__
http://www.informatimago.com/
Do not adjust your mind, there is a fault in reality.


Avatar
Pascal Bourguignon
Stephane CHAZELAS writes:

Le 07 Oct 2003 22:59:30 +0200, Pascal Bourguignon écrivait :
[...]
find . -name '*.ini' -type f -exec sh -c '
cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1"' {} {} ;
[...]



Mais j'ai l'impression qu'on y forke plus qu'avec ma solution:

find . -name *.init -type f -print
| while read f ; do cp $f $f~ && tr -d 15 < $f~ > $f ; done


On n'y forke pas plus (1 + 2n dans les deux cas voire 2 + 2n
pour la tienne, faudra peut-etre remplacer "tr" par "exec tr"
avec la mienne pour éviter le 1 + 3n avec certains sh), mais on
lance plus de commandes (1 shell par fichier).


Voyons voir:

$ touch toto.ini titi.ini tata.ini tutu.ini
$ find . -name '*.ini' -type f
-exec echo sh -c 'cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1"' {} {} ;

sh -c cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1" ./tutu.ini ./tutu.ini
sh -c cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1" ./tata.ini ./tata.ini
sh -c cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1" ./toto.ini ./toto.ini
sh -c cp "$1" "${1}~" && tr -d 15 < "${1}~" > "$1" ./titi.ini ./titi.ini

Donc: 1 fork pour le find
n forks pour les sh
n forks pour les cp
n forks pour les tr
----
1+3n forks


find . -name *.ini -type f -print
| while read f ; do echo cp $f $f~ && tr -d 15 < $f~ > $f ; done

cp ./tutu.ini ./tutu.ini~ && tr -d 015 < ./tutu.ini~ > ./tutu.ini
cp ./tata.ini ./tata.ini~ && tr -d 015 < ./tata.ini~ > ./tata.ini
cp ./toto.ini ./toto.ini~ && tr -d 015 < ./toto.ini~ > ./toto.ini
cp ./titi.ini ./titi.ini~ && tr -d 015 < ./titi.ini~ > ./titi.ini

Soit: 1 fork pour le find
n forks pour les cp
n forks pour les tr
----
1+2n forks


Comment ça se fait, je ne trouve pas le même résultat que toi?

De toutes façons, l'option de find est pourtant claire: '-exec'. Faut
bien faire un fork avant chacun de ces -exec non?

--
__Pascal_Bourguignon__
http://www.informatimago.com/
Do not adjust your mind, there is a fault in reality.




1 2 3