4

I'm learning bash redirections and wonder if there a way to emulate tee command with cat using redirections. Is that possible? Or may be there are some other ways to do this.


Before asking about tee I just sought for a possibility to split one streem into two identical ones using bash expression. But it seems to be impossible.

2
  • Not so far as I know with cat; you would I believe have to write your own tool that reads from standard input and writes simultaneously to standard output and to a specified list of files. But then you will end up with a roll-your-own implementation of tee.
    – DopeGhoti
    Commented Jun 9, 2017 at 21:27
  • You can in zsh with the MULTIOS option set, but I don't think you can hack it into bash.
    – Kevin
    Commented Jun 9, 2017 at 22:05

3 Answers 3

5

Provided that the Here is a bash function with the same functionality as tee and tee -a, provided the file does not contain ¤¤¤¤¤¤¤¤¤¤! (you can adjust this string to your liking, to make it highly unlikely that a file will ever contain it):

tee(){
  [ "$1" = '-a' ] && shift || rm -f "$1"
  sed 's/\x0/¤¤¤¤¤¤¤¤¤¤!/g' - | while read line; do
    echo "$line" | sed 's/¤¤¤¤¤¤¤¤¤¤!/\x0/g' >> "$1"
  done
}

Tested with bash and zsh.

Example use:

echo asdf | tee -a file
4
  • @roaima, +1, and see also Understanding "IFS= read -r line" and Why is printf better than echo?. Using </dev/stdin is also wrong, especially on Linux. Commented Dec 16, 2021 at 9:40
  • 1
    Without -a, the tee command truncates and overwrites the destination rather than removing it. (You lose permissions, ownerships, etc. if you rm.) I'd change rm -f "$1" to a simple >"$1" Commented Dec 16, 2021 at 13:26
  • This script removes spaces following a newline character. It also doesn't handle tee file1 file2 file3... nor does it copy its stdin to stdout. Commented Dec 16, 2021 at 13:27
  • 1
    Alexander, I declined your flag to remove Stéphane's comment, as I think there's still value in using printf over echo when dealing with arbitrary data like we are here. There's also good advice in there about the while read construct.
    – Jeff Schaller
    Commented Dec 16, 2021 at 13:30
2

You can not do it in bash with redirections, but you can do something like this:

$ cat file1
europe|EU
australia|AU
china|CN

$ cat file1 | { IFS=$'\n' read -r -d '' -a a; printf '%s\n' "${a[@]}"; printf '%s\n' "${a[@]}" >teetest; }
europe|EU
australia|AU
china|CN

$ cat teetest
europe|EU
australia|AU
china|CN

Alternative:

$ { IFS=$'\n' read -r -d '' -a a; printf '%s\n' "${a[@]}"; printf '%s\n' "${a[@]}" >teetest; } <file1

Or you can built a function:

$ function teetest { v="$(</dev/stdin)";printf '%s\n' "$v";printf '%s\n' "$v">teetest; }

$ cat file1 |teetest
europe|EU
australia|AU
china|CN

$ cat teetest
europe|EU
australia|AU
china|CN
1
  • These work only for text files. Specifically, character 0 (NULL) will not be copied Commented Dec 16, 2021 at 9:42
2

With zsh, yes, not with bash.

With zsh:

cat file.in > file1.out > file2.out

or even:

< file.in > file1.out > file2.out

or with brace expansion to generate the output files:

< file.in > file{1..2}.out

(running $NULLCMD: cat by default)

Note that cat has only one stdout which it writes the content of file.in to. Above, cat's stdout is a pipe. And zsh reads that output from the other end to write it to both file1.out and file2.out.

In other words, there's no magic there, zsh is just implementing tee internally.

1
  • Thank you, Stéphane, for information about zsh. I'm studying redirections using your valuable examples and tutorials. Commented Jun 11, 2017 at 16:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .