You need some way for the receiving end to recognize the end of the transferred file. With
cat file - | nc in the sending side, the data stream through the pipe will make no separation between the contents of the file, and whatever the user types on the terminal (
cat - reads the terminal). Also, by default, netcat doesn't react to an EOF on its input, but we can use
-q to have it send the EOF along.
So, the receiving script could be something like this:
cat > "$filename"
hash=$(shasum "$filename" |sed -e 's/ .*//')
printf "received file \"%s\" with SHA-1 hash %s\n" "$filename" "$hash"
cat reads the input until EOF, saving the file. Whatever follows, is executed after the file is received. Here, the script sends back the SHA-256 hash of the data it received.
Then, on the receiving side, run:
$ nc.traditional -l -p 12345 -c "./receivefile.sh somefilename"
and on the sending side:
$ cat sourcefile | nc.traditional -q 9999 localhost 12345
received file "somefilename" with SHA-1 hash 3e8a7989ab68c8ae4a9cb0d64de6b8e37a7a42e5
The script above takes the file name as an argument, so I used
-c instead of
-e, but of course you can also redirect the output of
nc to the destination file, as you did in the question.
If you want to send the filename from the sending side too, you could do something like this on the receiving side:
read -r filename
[[ $filename = */* ]] && exit 1 # exit if the filename contains slashes
cat > "$filename"
echo "received $filename"
and then send with
(echo filename; cat filename) | nc -q 9999 localhost 12345. (You may want to be way more careful with the remote-supplied filename, here. Or just use something designed for file transfer, like
Instead of using
nc, you could do this similarly with SSH:
cat filename | ssh user@somehost receive.sh