POSIX sh(1) es capaz de realizar varias operaciones de descriptor de archivo (equivalente a open(2) , close(2) y dup(2) , etc. ), así como también read una sola línea desde STDIN.
Así que tuve la impresión de que podemos reemplazar cat(1) con un script de shell compatible con POSIX, pero no he encontrado una implementación real. ¿Es realmente posible, o qué función de cat(1) podría faltar en sh(1) ? (Olvídate de las extensiones GNU por ahora)
No me preguntes por qué quiero hacer eso. ¿Como un cuestionario intelectual, tal vez?
cat puede copiar cualquier archivo a stdout; el archivo no necesita ser un archivo de texto. Puede incluir NUL , por ejemplo, y un NUL no se puede representar en una cadena sh . Así que definitivamente sería una característica de cat que sería muy difícil, si no imposible, de implementar. [Nota 1]
Aparte de eso, debería poder envolver una read y un echo dentro de un ciclo while , aunque hay algunos problemas complicados. (Reproducir con precisión un archivo no vacío que no termina en una nueva línea, por ejemplo).
Pero, técnicamente, el echo no es más parte de sh que el cat ; al igual que cat , es una utilidad que podría no estar presente (en un sistema que no sea Posix). En la práctica, los entornos sin echo son tan probables como los entornos sin cat ; si tiene sh , tiene una expectativa razonable de encontrar las utilidades de línea de comando estándar.
La única opción aceptada por una read mínima compatible con Posix es -r . Sin embargo, si tuviéramos la implementación bash de read , podríamos copiar un archivo carácter por carácter, aunque el carácter NUL nunca aparecerá en una variable de shell:
while IFS= read -d '' -rn1 char; do if [ -z "$char" ]; then printf '\0'; else printf '%s' "$char"; fi done < "$1" > "$2"Ejemplo:
$ printf 'foo\0bar\n\nbye' | > while IFS= read -d '' -rn1 char; do > if [ -z "$char" ]; then printf '\0'; else printf '%s' "$char"; fi > done | > hd 00000000 66 6f 6f 00 62 61 72 0a 0a 62 79 65 |foo.bar..bye| 0000000c El conjunto completo de opciones para read en esa invocación está cuidadosamente diseñado para trabajar en torno a una variedad de idiosincrasias en la implementación de bash:
IFS= evita que los caracteres de espacio en blanco finales se eliminen del resultado.-n1 hace que se lea un carácter, hasta el delimitador. Intuitivamente, -N1 sería más natural, ya que -N1 ignora el delimitador. Sin embargo, read también elimina los caracteres NUL de la entrada. Dado que la intención es almacenar cero caracteres en $char si el siguiente carácter es NUL , podemos evitar el problema usando -n1 y configurando el delimitador en NUL , lo que funciona porque la verificación del delimitador se realiza antes de que se eliminen los NUL .-d '' establece el carácter delimitador de línea en NUL . Véase más arriba.-r evita que \ se interprete en el flujo de entrada; esta es la única opción compatible con Posix en el conjunto. No hace falta decir que lo anterior es solo de interés teórico, o como un cuestionario intelectual según el OP. En la práctica, un script de shell no debería hacer más que coordinar el trabajo de las utilidades externas, y la existencia de utilidades compatibles con Posix, como cat , dd , head and tail , debería ser suficiente para cualquier necesidad de copia de archivos.
(Esto es esencialmente lo mismo que la respuesta de @rici, pero con un ejemplo concreto de un archivo que no se puede mostrar solo con sh ).
cat no se puede replicar usando sh solo. Esto se debe a que sh no proporciona ningún método para mover bytes de un archivo a otro que no involucre un parámetro de shell, y los parámetros de shell no pueden contener bytes NULL.
Aquí hay un ejemplo simple:
printf 'foo\0bar\n' > tmp.txt # Create a file containing a null byte IFS= read -r line < tmp.txt # Real that line into a variable. echo "$line" # Only outputs "foo"