En un script de shell ...
¿Cómo capturo stdin a una variable sin quitar ninguna línea nueva?
En este momento he intentado:
var=`cat`
var=`tee`
var=$(tee)
En todos los casos $var
no tendrá la nueva línea final de la secuencia de entrada. Gracias.
TAMBIÉN: Si no hay una nueva línea final en la entrada, entonces la solución no debe agregar una .
ACTUALIZACIÓN A LA LUZ DE LA RESPUESTA ACEPTADA:
La solución final que utilicé en mi código es la siguiente:
function filter() {
#do lots of sed operations
#see https://github.com/gistya/expandr for full code
}
GIT_INPUT=`cat; echo x`
FILTERED_OUTPUT=$(printf '%s' "$GIT_INPUT" | filter)
FILTERED_OUTPUT=${FILTERED_OUTPUT%x}
printf '%s' "$FILTERED_OUTPUT"
Si desea ver el código completo, consulte la página de github para expandr , un pequeño script de shell de filtro de expansión de palabras clave git de código abierto que desarrollé para fines de seguridad de la información. De acuerdo con las reglas establecidas en archivos .gitattributes (que pueden ser específicos de la rama) y git config , git canaliza cada archivo a través del script de shell expandr.sh cada vez que lo ingresa o sale del repositorio. (Es por eso que fue fundamental preservar las nuevas líneas finales, o la falta de ellas). Esto le permite limpiar información confidencial e intercambiar diferentes conjuntos de valores específicos del entorno para pruebas, puesta en escena y ramas vivas.
filter
tomastdin
, corresed
. Se capturastdin
en la$GIT_INPUT
continuación de impresión que de nuevo astdout
través de una tubería defilter
y coger sustdout
en$FILTERED_OUTPUT
y luego imprimirlo de nuevo astdout
. Todas las 4 líneas en la parte inferior de su ejemplo anterior podría ser reemplazado con sólo esto:filter
. Sin ofender aquí, es solo que ... estás trabajando muy duro. No necesita las variables de shell la mayor parte del tiempo, solo dirija la entrada al lugar correcto y páselo.filter
, agregará caracteres de nueva línea al final de cualquier flujo de entrada que inicialmente no termine en nuevas líneas. De hecho, originalmente lo hice,filter
pero me encontré con ese problema que me llevó a esta solución porque ni "agregar nuevas líneas" ni "siempre quitar nuevas líneas" son soluciones aceptables.sed
probablemente hará la nueva línea adicional, pero debe manejar esofilter
no con todo el resto. Y todas esas funciones que tienes básicamente hacen lo mismo: ased s///
. Está utilizando el shell para canalizar los datos que ha guardado en su memoria, desed
modo quesed
podría reemplazar esos datos con otros datos que el shell haya almacenado en su memoria parased
poder volver a canalizarlos al shell. ¿Por qué no solo[ "$var" = "$condition" ] && var=new_value
? Tampoco obtengo las matrices: ¿está almacenando el nombre de la matriz y[0]
luego losed
reemplaza con el valor[1]
? Tal vez chatear?filter
? Funciona perfectamente como está. Con respecto a cómo funciona el código en mi enlace y por qué lo configuré de la manera en que lo hice, sí, hablemos de ello en una sala de chat.Respuestas:
Las nuevas líneas finales se eliminan antes de que el valor se almacene en la variable. Es posible que desee hacer algo como:
y usar en
${var%x}
lugar de$var
. Por ejemplo:Tenga en cuenta que esto resuelve el problema de las nuevas líneas finales, pero no el byte nulo (si la entrada estándar no es texto), ya que de acuerdo con la sustitución del comando POSIX :
Pero las implementaciones de shell pueden preservar bytes nulos.
fuente
Puede usar el
read
incorporado para lograr esto:Para que un script lea STDIN, simplemente sería:
Sin embargo, no estoy seguro de en qué proyectiles funcionará esto. Pero funciona bien tanto en bash como en zsh.
fuente
-d
la sustitución del proceso (<(...)
) son portátiles; este código no funcionarádash
, por ejemplo.-d
, por eso puse el descargo de responsabilidad en la parte inferior. El OP no especifica el shell.dash
. Simplemente use<<HEREDOC\n$(gen input)\nHEREDOC\n
- indash
- que usa tuberías para heredocs de la misma manera que otros shells los usan para la sustitución de procesos: no hay diferencia. Laread -d
cuestión es solo especificar un delimitador: puede hacer lo mismo una docena de formas, solo asegúrese de hacerlo. Aunque necesitarás algo de colagen input
.IFS=''
probablemente no sea necesario. Está destinado para queread
no colapsen espacios. Pero cuando se lee en una sola variable, no tiene ningún efecto (que pueda recordar). Pero me siento más seguro dejándolo encendido :-)Puedes hacer como:
Cualquier cosa que hagas
$var
desaparece tan pronto como salgas de esa{ current shell ; }
agrupación de todos modos. Pero también podría funcionar así:fuente
$var
en la{ ... }
agrupación, no siempre es posible. Por ejemplo, si este comando se ejecuta dentro de un bucle y se necesita$var
fuera del bucle.{}
llaves. Es cierto, y se señala explícitamente en la respuesta , que$var
es muy probable que el valor de desaparezca por completo cuando la{ current shell; }
agrupación es cerrado. ¿Hay alguna forma más explícita de decirlo que, lo que sea que hagas$var
desaparece ...?input | sed "s/'"'/&"&"&/g;s/.*/process2 '"'-> &'/" | sh
_variables
función debash_completion
, que almacena el resultado de una sustitución de comando en una variable globalCOMPREPLY
. Si se usara una solución de tubería para mantener nuevas líneas, el resultado se perdería. En su respuesta, uno tiene la impresión de que ambas soluciones son igualmente buenas. Además, debe tenerse en cuenta que el comportamiento de la solución de canalización depende en gran medida del shell: un usuario podría probarecho foo | { var=$(sed '$s/$/./'); var=${var%.}; } ; echo $var
con ksh93 y zsh, y piensa que está bien, mientras que este código tiene errores.$var
desaparece" (lo que en realidad no es cierto, ya que esto depende del shell; POSIX no especifica el comportamiento), que es una oración bastante neutral. La segunda solución es mejor porque no sufre este problema y su comportamiento es consistente en todos los shells POSIX.