#1 Le 01/09/2021, à 20:44
- Hizoka
[Résolu] Récupérer stdin et conserver les \0
Bonsoir,
je cherche un moyen de lire stdin tout en conservant les \0.
Pour être clair :
Soit la fonction
popo()
{
stdin=$(< /dev/stdin)
echo "stdin : $stdin"
}
Si on l'appelle :
echo "bonjour" | popo
stdin : bonjour
echo -e "bonjour\ntoi" | popo
stdin : bonjour
toi
Mais si on veut ajouter un caractère null qui me servirait de séparateur :
echo -e "bonjour\0toi" | popo
bash: avertissement : substitution de commande: octet nul ignoré en entrée
stdin : bonjourtoi
Pour un exemple plus réaliste :
find . -print0 | popo
bash: avertissement : substitution de commande: octet nul ignoré en entrée
...
Comment faire pour conserver ces caractères ?
La seule solution que j'ai trouvée :
popo()
{
stdin="$(tr '\0' '@' < /dev/stdin)"
echo "stdin : $stdin"
}
echo -e "bonjour\0toi" | popo
stdin : bonjour@toi
Mais ça me force à le remplacer d'office et par un unique caractère qui pourrait être présent dans le stdin...
Que je sois forcé de faire le remplacement par défaut, OK mais il faudrait que je puisse faire une suite de caractères (sed ne fonctionne pas, j'ai la même erreur).
Le top serait pour le coup de pouvoir récupérer stdin proprement...
Il est également impossible de s'amuser à lire plusieurs fois le stdin pour vérifier en amont si le caractere de remplacement (@ dans l'exemple ci dessus) est déjà présent (pour en choisir un autre).
popo()
{
stdin=$(< /dev/stdin)
echo "stdin : $stdin"
stdin2=$(< /dev/stdin)
echo "stdin2 : $stdin2"
}
echo "bonjour" | popo
stdin : bonjour
stdin2 :
Une idée ?
Merci beaucoup et bonne soirée à vous.
Dernière modification par Hizoka (Le 03/09/2021, à 13:59)
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#2 Le 01/09/2021, à 22:45
- Watael
Re : [Résolu] Récupérer stdin et conserver les \0
un élément de réponse : https://stackoverflow.com/a/42493691
Connected \o/
Welcome to sHell. · eval is evil.
Hors ligne
#3 Le 02/09/2021, à 09:08
- Hizoka
Re : [Résolu] Récupérer stdin et conserver les \0
J'avais vu ce post mais ça ne répond pas vraiment à mon besoin.
Mais j'ai essayé d'en repartir quand même :
popo()
{
# Pour ne pas rester bloqué si aucun stdin
test -t 0 && return
# Création d'un array en utilisant \0 comme séparateur
mapfile -td '' stdinArray < /dev/stdin
# Si l'array contient plusieurs éléments, c'est qu'il y avait des \0
if (( ${#stdinArray[@]} > 1))
then
echo "Il y avait des nulls"
stdin="$(printf '%s\0' "${stdinArray[@]}")"
# Sinon, c'est qu'il n'y avait pas de \0
else
echo "Aucun null"
stdin="${stdinArray[0]}"
fi
echo -e "stdin : $stdin"
}
echo -e "bonjour\ntoi" | popo
Aucun null
stdin2 : bonjour
toi
echo -e "bonjour toi" | popo
Aucun null
stdin2 : bonjour toi
echo -e "bonjour\0toi" | popo
Il y avait des nulls
bash: avertissement : substitution de commande: octet nul ignoré en entrée
stdin2 : bonjourtoi
Le mapfile permet la découpe sans problème.
Mais le printf provoque aussi ce même message d'erreur.
En remplaçant le printf, Je n'ai plus de message d'erreur mais la variable s'arrête au \0 :
popo()
{
# Pour ne pas rester bloqué si aucun stdin
test -t 0 && return
# Création d'un array en utilisant \0 comme séparateur
mapfile -td '' stdinArray < /dev/stdin
# Si l'array contient plusieurs éléments, c'est qu'il y avait des \0
if (( ${#stdinArray[@]} > 1))
then
echo "Il y avait des nulls"
## LA MODIFICATION EST ICI
printf -v stdin '%s\0' "${stdinArray[@]}"
## LA MODIFICATION EST ICI
# Sinon, c'est qu'il n'y avait pas de \0
else
echo "Aucun null"
stdin="${stdinArray[0]}"
fi
# Tests
echo "$stdin" | hexdump
echo -e "stdin : $stdin"
printf '%s' "$stdin"
}
echo -e "bonjour\0toi" | popo
Il y avait des nulls
0000000 6f62 6a6e 756f 0a72
0000008
stdin : bonjour
bonjour
D'ailleurs j'ai les mêmes résultats en faisant simplement :
liste=(a z e r t y)
a="$(printf '%s\0' "${liste[@]}")"
bash: avertissement : substitution de commande: octet nul ignoré en entrée
echo "$a"
azerty
echo "$a" | hexdump
0000000 7a61 7265 7974 000a
0000007
liste=(a z e r t y)
printf -v a '%s\0' "${liste[@]}"
echo "$a"
a
echo "$a" | hexdump
0000000 0a61
0000002
Pour réussir à remettre en place les \0 (dans le cas ou je ne dois pas les toucher), j'en arrive à ça :
popo()
{
# Pour ne pas rester bloqué si aucun stdin
test -t 0 && return
# Création d'un array en utilisant \0 comme séparateur
mapfile -td '' stdinArray < /dev/stdin
# Si l'array contient plusieurs éléments, c'est qu'il y avait des \0
if (( ${#stdinArray[@]} > 1))
then
echo "Il y avait des nulls"
# J'ajoute des | comme séparateur
printf -v stdin '%s|' "${stdinArray[@]}"
# Je vire le dernier | et le saut de ligne qui peut venir de l'echo du pipe
stdin="${stdin/%|}"
stdin="${stdin/%$'\n'}"
# Je remplace les | par des \0
stdin="${stdin//|/\\0}"
# Sinon, c'est qu'il n'y avait pas de \0
else
echo "Aucun null"
stdin="${stdinArray[0]}"
fi
# Tests
echo "$stdin" | hexdump
echo -e "stdin : $stdin"
printf '%s' "$stdin"
}
echo -e "bonjour\0toi" | popo
Il y avait des nulls
0000000 6f62 6a6e 756f 5c72 7430 696f 000a
000000d
stdin : bonjourtoi
bonjour\0toi
On peut aussi faire :
popo()
{
# Pour ne pas rester bloqué si aucun stdin
test -t 0 && return
# Création d'un array en utilisant \0 comme séparateur
mapfile -td '' stdinArray < /dev/stdin
# Si l'array contient plusieurs éléments, c'est qu'il y avait des \0
if (( ${#stdinArray[@]} > 1))
then
echo "Il y avait des nulls"
# Je boucle sur les items
for item in "${stdinArray[@]}"
do
# Je traite différement le dernier element et les autres
if [[ $item != "${stdinArray[-1]}" ]]
then
# J'ajoute le \0 comme séparateur
stdin+="${item}\0"
else
# Je supprime le saut de ligne de fin possiblement ajouté par le echo du pipe
stdin+="${item/%$'\n'}"
fi
done
# Sinon, c'est qu'il n'y avait pas de \0
else
echo "Aucun null"
stdin="${stdinArray[0]}"
fi
# Tests
echo "$stdin" | hexdump
echo -e "stdin : $stdin"
printf '%s' "$stdin"
}
echo -e "bonjour\0toi" | popo
Il y avait des nulls
0000000 6f62 6a6e 756f 5c72 7430 696f 000a
000000d
stdin : bonjourtoi
bonjour\0toi
Donc au final ça semble marcher mais ça me parait compliqué,
Penses tu que ce soit solide et vraiment fonctionnel ?
Penses tu qu'il y ait moyen de simplifier le truc ?
PS, c'est dingue comment j'avance mieux en postant et c'est pourtant pas faute d'avoir fait des tests avant...
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#4 Le 02/09/2021, à 22:56
- kamaris
Re : [Résolu] Récupérer stdin et conserver les \0
Non ça ne marche pas en général, puisque si ton entrée contient le ou les caractères dont tu te sers pour représenter le caractère nul dans la variable stdin, il y aura confusion lors du remplacement ou de la lecture.
D'ailleurs, si tu pouvais faire cette hypothèse que tu ne rencontreras pas ce ou ces caractères dans l'entrée, tu pourrais t'en servir directement comme séparateur dès le départ, plutôt que de passer par le caractère nul qui ne sert du coup à rien.
Mais pourquoi vouloir à tout prix mettre tout ça dans une variable chaine ?
Le tableau est fait pour ça : tu stockes l'info sous cette forme, et quand tu veux la réutiliser sous sa forme originelle (donc nécessairement sous la forme d'un flux), tu fais
printf '%s\0' "${tableau[@]}" | …
Dernière modification par kamaris (Le 02/09/2021, à 22:58)
Hors ligne
#5 Le 03/09/2021, à 09:39
- Hizoka
Re : [Résolu] Récupérer stdin et conserver les \0
Tu n'as pas tord, je me prends surement la tête pour rien, du coup, j'essaie de virer la variable qui était temporaire.
Je refais une explication car je suis moi même un peu perdu
1) Je récupère stdin tel quel.
Mais vu que les \0 sautent, je les utilise comme séparateur pour savoir où ils se trouvent s'il y en a pour pouvoir les renvoyer à la fin si besoin.
mapfile -td '' stdinTemp < /dev/stdin
2) Je regarde le délimiteur indiqué.
Si c'est \0, mon tableau final ne change pas du 1er.
Proposals=("${stdinTemp[@]}")
Si ce n'est pas \0, je recrée un tableau en utilisant le délimiteur voulu
mapfile -td "${Delimiter}" Proposals < <(printf '%s\0' "${stdinTemp[@]}")
3) J'affiche mon tableau après pas mal de modifications.
4) Je renvoie les lignes demandées.
Mais mapfile n'a pas l'air d'aimer les \0
tab=("ab" "a_b" "a b")
mapfile -td "_" Proposals < <(printf '%s\0' "${tab[@]}")
for x in "${Proposals[@]}"; do echo -e "x : $x : $(echo "$x" | hexdump)"; done
x : ab : 0000000 6261 000a
0000003
x : b : 0000000 0a62
0000002
Il n'affiche pas ce qui se trouve après les \0.
Du coup, ce que je vois comme meilleur moyen, c'est de remplacer les \0 par ----Hizo---- par ex (pour être sûr que ça ne sera pas utiliser).
mapfile -td "_" Proposals < <(printf '%s----Hizo----' "${tab[@]}")
Lorsque j'affiche les lignes, je supprime ces caractères.
echo -e "${Ligne//----Hizo----}"
Lorsque je renvoie le texte, je remplace ces caractères par \0.
echo -e "${Ligne//----Hizo----/\\0}"
Qu'en penses tu ?
Dernière modification par Hizoka (Le 03/09/2021, à 09:40)
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#6 Le 03/09/2021, à 10:31
- kamaris
Re : [Résolu] Récupérer stdin et conserver les \0
Je ne suis pas sûr de comprendre ce que tu veux faire, et donc si l'histoire du "séparateur secondaire" que tu fais intervenir en 2) est vraiment nécessaire.
Mais si elle l'est, et s'il faut aussi garder trace de là où se trouvent les séparateurs initiaux (les caractères nuls), je ne vois pas d'autre manière de travailler proprement que de boucler sur les éléments du tableau initial, en travaillant sur chaque élément avec le séparateur secondaire :
# on stocke l'entrée initiale
mapfile -td '' stdinTemp < /dev/stdin
# on travaille avec un autre séparateur, par exemple "_", dans des sous-tableaux
for n in "${!stdinTemp[@]}"; do
# sous-tableau auquel on va appliquer des transformations
mapfile -td '_' subArr <<<"${stdinTemp[n]}"
# transformations
…
# mise à jour du tableau initial
stdinTemp[n]=$(IFS='_'; echo "${subArr[*]}")
done
# affichage de l'entrée mise à jour, avec ses séparateurs initiaux
printf '%s\0' "${stdinTemp[@]}" | …
Hors ligne
#7 Le 03/09/2021, à 11:21
- Hizoka
Re : [Résolu] Récupérer stdin et conserver les \0
En fait à la base c'était tout simple, je reçois un texte que je découpe en fonction d'un délimiteur (\n par défaut pour mapfile).
C'était facile jusque là.
Mais il s'avère que si dans le texte d'entrée il y a des \0 (que ce soit par besoin ou par erreur de l'utilisateur), je les perds même si \n est toujours le délimiteur à utiliser.
Je suis donc obligé de traiter la possible présence des \0 avant de gérer mon délimiteur habituel pour pouvoir les renvoyer à la fin.
Ce que tu me proposes me semble super compliqué...
echo "Joe
Avrell
Luke" | ma commande
affichera par exemple :
Bonjour, quel est votre nom ?
1) Joe
2) Avrell
3) Luke
Votre réponse : 1
Joe
et si on complique (c'est vraiment pour l'exemple) :
echo "Joe\0Dalton
Avrell\0Dalton
Luke\0Lucky" | ma commande
Bonjour, quels sont les méchants ?
1) JoeDalton
2) AvrellDalton
3) LukeLucky
Votre réponse : 1 2
# et c'est là qu'il faut bien renvoyer les \0
Joe\0Dalton
Avrell\0Dalton
Je pense que je vais rester sur le système :
1) Je remplace les \0 par -----Hizo-----
2) Lorsque je dois afficher la liste de choix, je supprime -----Hizo-----
3) Lorsque je renvoie le texte choisi, je remplace -----Hizo----- par \0
En tout cas merci pour tes remarques qui m'ont permis de simplifier le code, pour ta présence sur le forum ainsi que pour tes nombreuses propositions
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#8 Le 03/09/2021, à 11:59
- kamaris
Re : [Résolu] Récupérer stdin et conserver les \0
De rien
Quelques remarques pour finir :
Il ne devrait pas y avoir de caractère nul dans un texte, donc on peut se demander si le respect de cette condition ne devrait pas être assuré dès le départ, en affichant si besoin un avertissement indiquant le traitement qui leur sera réservé par la suite (ou en sortant en erreur).
Ultimement, si tu ne peux pas être sûr qu'il n'y aura pas de caractère nul dans le "texte" d'entrée, tu ne peux à fortiori pas être sûr que la chaine "-----Hizo-----", ou toute autre chaine ou caractère, ne seront pas présents. D'où la solution imaginée ci-dessus, mais en pratique, j'en reviendrais personnellement au premier point : assurer des hypothèses convenables pour l'exécution du programme.
Hors ligne
#9 Le 03/09/2021, à 13:09
- Watael
Re : [Résolu] Récupérer stdin et conserver les \0
- l'utilisateur entre UNE donnée à la fois, et pas une liste tout d'un coup.
- je vérifie/corrige/valide le format de la donnée (éventuellement je demande de saisir à nouveau la donnée).
- j'ajoute la donnée à un tableau.
- quand l'utilisateur a fini d'entrer toutes ses données, je passe le tableau (tout propre) au traitement suivant.
entre
donner tout de suite un cadre à la saisie, ou
corriger toutes* les erreurs imaginables,
je préfère ne pas perdre de temps.
--
* j'en oublierai toujours une
Connected \o/
Welcome to sHell. · eval is evil.
Hors ligne
#10 Le 03/09/2021, à 13:59
- Hizoka
Re : [Résolu] Récupérer stdin et conserver les \0
L'idée n'est pas d'envoyer une donnée mais de se brancher sur un pipe et pourquoi pas directement sur un find par exemple.
Dans ce genre de cas, je suis bien obligé de tout traiter la liste d'un coup.
Il ne devrait pas y avoir de caractère nul dans un texte, donc on peut se demander si le respect de cette condition ne devrait pas être assuré dès le départ, en affichant si besoin un avertissement indiquant le traitement qui leur sera réservé par la suite (ou en sortant en erreur).
Je ne veux pas spécialement bloquer cette possibilité même si dans les faits, ça sera plus que rare je pense.
Mais on peut imaginer un truc du genre :
1) Bogdanoff (qui renverra le texte Bogdanoff\0Igor\0Grichka)
2) Dalton (qui renverra Dalton\0Joe\0Avrell\0William\0Jack)
Choix : 1 2
L'utilisateur récupérer 2 lignes dont il peut facilement séparer le contenu.
L'idée est aussi de pousser le truc à fond et surtout, ça me permet d'apprendre de nouvelles choses et ça c'est cool
tu ne peux à fortiori pas être sûr que la chaine "-----Hizo-----", ou toute autre chaine ou caractère, ne seront pas présents.
Au pire une petite boucle avec une liste de possibilité qui est testée et quand j'en trouve une non utilisée, je la choisis comme séparateur.
Ou même remplacer les -----Hizo----- présents par un truc encore plus compliqué (1p@ule/1mµr...) que je rechange en -----Hizo----- quand j'ai besoin...
Mais je vais préciser dans le man que -----Hizo----- ne doit pas être utilisé
Encore merci à vous 2, je clôture le sujet car tous mes tests sont fonctionnels avec ce que j'ai mis en place.
C'est toujours un plaisir de lire vos interventions !
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne