tee: save it and see it
tee is a T-junction in a pipeline: it writes what it receives into a file and passes it on, so you still see it:
ls /etc | head -n 3 | tee first3.txt
cat first3.txtIn the terminal
student@vulntech:~$ ls /etc | head -n 3 | tee first3.txt acpi alpine-release apk student@vulntech:~$ cat first3.txt acpi alpine-release apk
tee -a appends instead of replacing, like >>:
echo "first check" | tee -a checks.log
echo "second check" | tee -a checks.log
cat checks.logIn the terminal
student@vulntech:~$ echo "first check" | tee -a checks.log first check student@vulntech:~$ echo "second check" | tee -a checks.log second check student@vulntech:~$ cat checks.log first check second check
Writing to a protected file: sudo tee
Here's a puzzle. sudo runs a command as the administrator, yet this fails:
sudo echo "Welcome aboard" > /etc/motdIn the terminal
student@vulntech:~$ sudo echo "Welcome aboard" > /etc/motd -bash: /etc/motd: Permission denied
The redirection > is done by your shell, as you, before sudo even starts, and you can't write to /etc/motd. The fix is to let a program running as root do the writing: pipe the text into sudo tee.
echo "Welcome aboard" | sudo tee /etc/motd
cat /etc/motdIn the terminal
student@vulntech:~$ echo "Welcome aboard" | sudo tee /etc/motd [sudo] password for student: Welcome aboard student@vulntech:~$ cat /etc/motd Welcome aboard
Tip:
sudo tee -a fileadds to the end of a protected file. Add> /dev/nullif you don't want to see the text twice.
xargs: lines become arguments
Some commands, such as mkdir, rm and touch, don't read standard input: they want names as arguments. xargs reads lines and adds them to the end of a command:
cat dirs.txt
cat dirs.txt | xargs mkdir
ls -d */In the terminal
student@vulntech:~$ cat dirs.txt maps logs photos student@vulntech:~$ cat dirs.txt | xargs mkdir student@vulntech:~$ ls -d */ logs/ maps/ photos/
A classic: find files, then do something to all of them at once:
find cache -name '*.tmp'
find cache -name '*.tmp' | xargs rm
find cache -type fIn the terminal
student@vulntech:~$ find cache -name '*.tmp' cache/old/c.tmp cache/a.tmp cache/b.tmp student@vulntech:~$ find cache -name '*.tmp' | xargs rm student@vulntech:~$ find cache -type f cache/keep.txt
Useful xargs options
| Option | What it does |
|---|---|
-n 1 | Run the command once per item |
-I {} | Put each line wherever {} appears, instead of at the end |
-0 | Items are separated by a null character: use with find -print0, it's safe with spaces in file names |
echo a b c | xargs -n 1 echo item:
ls *.txt | xargs -I {} cp {} {}.bak
lsIn the terminal
student@vulntech:~$ echo a b c | xargs -n 1 echo item: item: a item: b item: c student@vulntech:~$ ls *.txt | xargs -I {} cp {} {}.bak student@vulntech:~$ ls crew.txt crew.txt.bak route.txt route.txt.bak
Common mistake: Plain
xargssplits names at spaces, somy notes.txtbecomes two names. If file names may contain spaces, usefind ... -print0 | xargs -0 ....
Remember:
tee filesaves and shows.sudo teewrites protected files.xargsturns lines into arguments.