Comment créer un script bash pour prendre en compte la ligne de commande (positionnels), la sortie standard (pipe) et la substitution de processus?

J’ai écrit un script qui prend un tas d’options (-d, -v, -l, ainsi que –version, –leader, etc …) et ensuite le rest du texte ($ *) peut être n’importe quoi . Le script traite le texte et le recrache après reformatage. C’est assez long, alors voici une version condensée:


## ————————- [myScript.sh] —————— ——– ##

(1) Définissez les valeurs par défaut:

declare -- v='1.0' FS=$':\n\r\v\f\t' Application='Finder' Files s='s' declare -i errors=0 element=0 counter=0 n L=2 

(2) Parse entrée utilisateur:

 until [[ -z "$1" || "$1" == '--' || "${1:0:1}" != '-' ]]; do [ "$Input" ] && unset Input if [[ "$1" =~ ^(-[Ww]|--[Ww]idth=)([0-9]+)? ]]; then Input=$(echo "$1" | gsed -re 's|--?W(idth=)?||I' | grep -Eoe '^(0|[1-9][0-9]*)$') [ -z "$Input" ] && echo "$2" | grep -Eoe '^(0|[1-9][0-9]*)$' && Input="$2" && shift 1 (( Input >= 0 )) && Width="$Input" || unset Width elif [[ "$1" =~ ^((-[LlIi]|--(([Ll]ead(er|ing)?)?([Ii]n(dent)?)|[Ll]ead(er|ing)?)=)([0-9]+)?)$ ]]; then Input=$(echo "$1" | gsed -re 's|--?[az]+=?||I' | grep -Eoe '^(0|[1-9][0-9]*)$') [ -z "$Input" ] && echo "$2" | grep -Eoe '^(0|[1-9][0-9]*)$' && Input="$2" && shift 1 (( Input >= 0 )) && L="$Input" || unset L ... else printf "$(Bold 'Error:') Unrecognized option: '$(Tbrown "$1")'\n\n" >&2 exit 2 fi shift 1 done 

(3) Maintenant, voici le texte:

 IFS='' [ -n "${*}" ] && declare Text="${*}" || Text="$(cat)" ## could also use read instead of cat ## [ -z "$Text" ] && printf "$(Bold 'Error:') No text entered...\n\n" >&2 && exit 2 

(4) Traiter le texte:

 Text="$(echo "$Text" | gsed -rne '1h;1!H;$g;s|[\x0A-\x0D]+| |g;$p' | expand -t4 )" echo "$Text" ## (temporary) ## exit 0 ## (temporary) ## ... ## (process text) ## ... ## (process more) ## 

La partie 3 fonctionne pour accepter le texte saisi après les options et lorsqu’il est redirigé, mais se bloque en attente de saisie si aucun texte n’est entré et ne voit pas le texte transmis comme substitution de processus … par exemple:

Exemples:

 ./myScript.sh -L10 --width=20 'This is a test'
> This is a test
echo 'This is a test' | ./myScript.sh -L10 --width=20
> This is a test
./myScript.sh -L10 --width=20 < <( echo 'This is a test' )
> This is a test
 ./myScript.sh -L10 --width=20 ## * Voulez-vous arrêter ceci * ##
> (No output)... hangs waiting on cat (or read) for a ^D
echo 'This is a test' >( ./myScript.sh )
> This is a test /dev/fd/63
> Error: No text entered...
./myScript.sh -L10 --width=20 <<<'This is a test'
> This is a test
echo "This is a test" | tee >( ./myScript.sh -L10 --width=20 ) >( ./myScript.sh )
> This is a test
> This is a test
> This is a test 

Comment puis-je obtenir le script de ne pas accrocher chat ou lire, en attente de saisie? (sans utiliser timeout ou read -t car cela ralentit les choses)?

Je pense que ce que vous voulez ici est d’éviter de lire dans un terminal:

 if test -t 0; then echo "Not reading from terminal. Pipe through cat if you really want to do this" >&2 exit 1 fi 

Il n’est pas possible de détecter le cas général où aucune source de données n’a été fournie spécifiquement pour le programme; toute redirection est effectuée par le shell, et votre programme ne peut pas dire si son entrée standard lui a été spécifiquement atsortingbuée ou héritée du shell.

Ça marche! Merci geekosaur! Avec votre aide, voici ma solution:

IFS='' if [ -n "${*}" ]; then declare Text="${*}" elif [ -t 0 ]; then TempT="$(mktemp -t 'Entered Text')" printf "\n$(Bold Enter text here:)\n\t** Note - To $(Tred stop) reading from stdin, enter $(Tred '/EOT')\n$(Twhite '∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞')\n" counter=0 until [[ "$line" == /[Ee][0Oo][Tt] ]]; do (( counter++ )) read -rp "$(printf "$(Twhite '⋇')$(Taqua "%%7s")$(Twhite '⋇ ')" "$(frame 0 7 "[$counter]" 0 - --no)")" -e line echo "$line" >>"$TempT" done readarray -t EnteredtText <"$TempT" ( set -u; [[ -f "$TempT" && "$TempT" =~ ^(/private)?/tmp/ ]] && { mv -f "$TempT" "$HOME/.Trash/" || rm -f "$TempT"; } ) &>'/dev/null' IFS=$'\n' declare Text="$(echo "${EnteredtText[*]}" | gsed -e '$d')" else Text="$(cat)" fi IFS='' if [ -n "${*}" ]; then declare Text="${*}" elif [ -t 0 ]; then TempT="$(mktemp -t 'Entered Text')" printf "\n$(Bold Enter text here:)\n\t** Note - To $(Tred stop) reading from stdin, enter $(Tred '/EOT')\n$(Twhite '∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞')\n" counter=0 until [[ "$line" == /[Ee][0Oo][Tt] ]]; do (( counter++ )) read -rp "$(printf "$(Twhite '⋇')$(Taqua "%%7s")$(Twhite '⋇ ')" "$(frame 0 7 "[$counter]" 0 - --no)")" -e line echo "$line" >>"$TempT" done readarray -t EnteredtText <"$TempT" ( set -u; [[ -f "$TempT" && "$TempT" =~ ^(/private)?/tmp/ ]] && { mv -f "$TempT" "$HOME/.Trash/" || rm -f "$TempT"; } ) &>'/dev/null' IFS=$'\n' declare Text="$(echo "${EnteredtText[*]}" | gsed -e '$d')" else Text="$(cat)" fi

Sur cette note, j’ai une autre question:

Est-il possible (je suis sûr que c’est le cas) de lire un bloc de texte à partir de stdin sans utiliser de fichier?

Cette boucle embêtante exécute un sous-shell. J’ai essayé quelques choses:

exec 7<&0 exec 0Temp ## or sub here? ## exec 0<&7 exec 7<&- mapfile Text

while read line; do [[ "$line" != '/EOT' ]] && readarray Text <( echo "$line" ) done

exec 7<&0 exec 0<( readarray -u7 Text ) until [[ "$line" == '/EOT' ]]; do read -u7 line done >&7 exec 0<&7 exec 7<&-

Peut-être quelque chose avec une commande here- << -type? J'ai écrit des scripts pour un peu moins de 2 ans et pour la vie de moi, je ne peux pas comprendre la redirection d'E / S (au-delà des bases). La relecture du chapitre dans de nombreux ouvrages / formulaires conduit toujours à une tonne de jeux autour des tests, je ne comprends pas [je pense que vous voyez;)], je suis frustré et finalement je trouve une autre façon de faire ce que je vouloir. Cette commande mystérieuse exec et tous les descripteurs de fichiers, à l'exception de 0/1/2, ne sont utilisés dans aucun de mes scripts ... mais je sais qu'il existe un moyen de répondre à cette question sans création de fichier ni suppression ultérieure.

Merci!