BSD Tips and Tricks
Take a screenshot the easy way:
Almost everybody has a movie player installed these days, and that movie player will usually bring in the ffmpeg
package. Therefore, I take my screenshots like this:
$ ffmpeg -loglevel fatal -f x11grab -i :0 -frames:v 1 ss.png
Blam!
(Change the extension to .jpg
and ffmpeg
will obligingly output a JPEG file.)
rvp Trying to get the output as the date format with
$ ffmpeg -loglevel fatal -f x11grab -i :0 -frames:v 1"$(echo $(date +'%d-%m-%Y.%H:%M'))".png
but this doesn't work... what am I doing wrong here?
rvp Cheers. So many ways of taking a screenshot... who knew
@kamil
This should probably be credited to @rvp although I did originally find it on youtube related to zsh
(although I cant seem to find it again).
alias h='fc -l 1 | awk "{ sub(/^[[:blank:]]*[0-9]+[[:blank:]]+/, \"\"); print }" | fzf | tr -d "\n" | xclip -selection c'
Requires fzf
but is a great way to search your ksh
history and copy previous commands to the clipboard,
pfr If you're going to recommend that pipeline, please use the simpler one which uses fc -n
and sed
. That awk
regex is just too much--what was I thinking!
rvp My bad, I didn't notice you had corrected your post to use sed
instead of awk
so I'm yet to try that.
Remove shell-style comments and empty/blank lines for display
#!/bin/sh
#
# nocom.sh: Display only non-blank non-commented lines.
exec sed -Ee '
/^#!\//b
/^[[:blank:]]*(#|$)/d' -- "$@"
Useful for exmining shell-scripts, config. files, etc. without the intervening clutter.
$ diff -u <(nocom.sh /etc/rc.conf.orig) <(nocom.sh /etc/rc.conf)
Use sed
to split a single patch file into multiple ones using line numbers
Let's say you have a patch file like this one and you want to split it into multiple patch files for some reason. This is easily done using sed
:
First, determine the line numbers. For a unified diff like the above:
$ grep -n -- '^diff ' ~/tmp/ueberzug-18.1.9.patch
1:diff -urN ueberzug-18.1.9.orig/Xshm/Xshm.c ueberzug-18.1.9/Xshm/Xshm.c
22:diff -urN ueberzug-18.1.9.orig/examples/fzfimg.sh ueberzug-18.1.9/examples/fzfimg.sh
34:diff -urN ueberzug-18.1.9.orig/ueberzug/query_windows.py ueberzug-18.1.9/ueberzug/query_windows.py
55:diff -urN ueberzug-18.1.9.orig/ueberzug/xutil.py ueberzug-18.1.9/ueberzug/xutil.py
$
Now, you can sed
to split that patch into 3 or 4 pieces (depending on whether you want a patch for each directory, or one for each file, respectively):
$ sed -n \
'1,21w 1.patch
22,33w 2.patch
34,54w 3.patch
55,$w 4.patch' \
~/tmp/ueberzug-18.1.9.patch
$ ls
1.patch 2.patch 3.patch 4.patch
$
Shell as interactive line-editor
If you're in a tight spot with no editor close by (or don't want to use the standard ones like ed(1)
or vi(1)
), you can use the shell's own line-editing features to create a file line-by-line (make sure you quote things correctly for your shell):
Using
echo(1)
:$ set -o emacs # EMACS-like line-editing $ echo 'test \! -r /etc/mk.conf' > x.sh # 1st line $ echo 'test \! -r /etc/pkg_install.conf' >> x.sh # 2nd line: up-arrow; edit ... # many lines later... $ chmod u+x x.sh
This is the simplest method.
Using
cat(1)
andhere-docs
:$ set -o emacs # EMACS mode $ cat > x.sh <<\EoF # read until an `EoF' > test \! -r /etc/mk.conf # 1st line > test \! -r /etc/pkg_install.conf # 2nd line: up-arrow; edit ... > EoF # done with here-doc $ chmod u+x x.sh
The problem with this is that every up-arrow bring in all the previously entered lines. So we come to, my preference, #3:
Using
exec
:$ sh # spawn new shell first $ set -o emacs # EMACS mode $ exec > x.sh # redirect all output to file $ echo 'test \! -r /etc/mk.conf' # 1st line # Note that no output is seen # because it all goes into the # file $ echo 'test \! -r /etc/pkg_install.conf' # 2nd line: up-arrow; edit ... $ <Ctrl-D> # exit "editor" shell $ chmod u+x x.sh
- Edited
rvp Possible alternative one line solutions for very short files:
$ { echo 'test \! -r /etc/mk.conf' ; echo 'test \! -r /etc/mk.conf'; } >> x.sh # using echo
$ printf '%s\n%s\n' 'test \! -r /etc/mk.conf' 'test \! -r /etc/mk.conf' >> x.sh # using printf
I find printf
particularly useful for introducing tab spaces; so for example, I often find myself using:
$ printf '%s\n\t%s\n' 'test \! -r /etc/mk.conf' 'test \! -r /etc/mk.conf' >> x.sh
which results in:
test \! -r /etc/mk.conf
test \! -r /etc/mk.conf
JuvenalUrbino Since printf
reuses the format specifier for each argument, you can shorten that first example some more:
$ printf '>>>%s<<<\n' a b c
>>>a<<<
>>>b<<<
>>>c<<<
$
Tracing shell scripts is pretty easy, you just do sh -vx script.sh
, but tracing startup scripts like ~/.profile
and ~/.shrc
take a bit more work. Here's some ready-made stuff:
If you want to trace ~/.profile
, you add these lines to the beginning and end of that file:
# tron
if [ "$DO_TRACE" = 1 ]
then set -vx
exec 8>&1 9>&2 >/tmp/sh.$$.log.txt 2>&1
fi
[...original contents of ~/.profile...]
# troff
if [ "$DO_TRACE" = 1 ]
then exec 1>&8 2>&9 8>&- 9>&-
set +vx
fi
then activate tracing by setting the variable DO_TRACE
to 1
before the shell starts:
$ env DO_TRACE=1 xterm -ls
The trace output is left in /tmp/sh.NNN.log.txt
, where NNN
is the pid
of the shell.
And, it might be more convenient to put the tracing code into separate files and source
them in each file that needs it instead of duplicating the stuff everywhere:
. $HOME/lib/tron
[...original script...]
. $HOME/lib/troff
Caveat: the script that you're tracing should not be using the file descriptors (8
and 9
) that we use for tracing. If it does, you'll have to change those to something else.
- Edited
To append some text to a file you can just use >>
in the shell:
$ echo text >> file
Some time back, I had to add some text to the penultimate (last-but-one) line:
$ tail /etc/rc.local
echo -n 'Starting local daemons:'
# Add your local daemons here, eg:
#
#if [ -x /path/to/daemon ]; then
# /path/to/daemon args
#fi
echo '.'
$
This script is what I cooked up:
pen.sh
#!/bin/sh
#
# pen.sh: Insert $1 as penultimate line.
set -eu -o pipefail
if [ $# -eq 0 ]
then echo >&2 "Insert \"text\" as penultimate line."
echo >&2 "Usage: ${0##*/} text [FILE...]"
exit 1
fi
case $(uname -s) in
FreeBSD|Linux) inpl="-i " ;; # for the love of POSIX,
NetBSD) inpl="-i" ;; # make up your minds...
esac
text=$(echo "$1" | sed 's/$/\\/g') # for multi-line "text"
text=${text%\\} # final `\' unwanted
shift
exec sed -e "\$i\\
$text" ${1+$inpl''} -- "$@"
Usage:
$ pen.sh 'test -d /var/empty || install -d -m 555 -o root -g wheel /var/empty' /etc/rc.local
$ tail /etc/rc.local
echo -n 'Starting local daemons:'
# Add your local daemons here, eg:
#
#if [ -x /path/to/daemon ]; then
# /path/to/daemon args
#fi
test -d /var/empty || install -d -m 555 -o root -g wheel /var/empty
echo '.'
$
Bonus: Adding text ("hello world") after some arbitrary line (let's say the 7th):
$ awk '{print} NR==7 {print "hello world"}' FILE
rvp Bonus: Adding text ("hello world") after some arbitrary line (let's say the 7th):
Copy$ awk '{print} NR==7 {print "hello world"}' FILE
Also (using built-in printf and ed):
$ printf '7i\nhello world\n.\nw\n' | ed -s FILE
- Edited
JuvenalUrbino Also (using built-in printf and ed):
It never crossed my mind that ed could be used in such way, this is beautiful.
For people unfamiliar with ed, I highly recommend this little gold nugget
Expanding further on this, I'd figure one could easily replace a block of text with another file using this technique.
For example, you have some inline css in multiple html files and would like to replace the following block of code:
<style>
body {
color: blue
}
</style>
with another file named red.css containing:
<style>
body {
color: red
}
</style>
For all html files in the current directory:
$ for file in *.html
$ do
$ printf '/style\n.,/style/d\n-r red.css\nw\n' | ed -s $file
$ done
Note:
I'd recommend to first test this on one file and write the changes to a new file in /tmp. Then run a diff to see what changes will be made before actually editing the files:
$ printf '/style\n.,/style/d\n-r red.css\nw /tmp/edit\n' | ed -s index.html && diff -bu index.html /tmp/edit
oui I'd recommend to first test this on one file and write the changes to a new file in /tmp. Then run a diff to see what changes will be made before actually editing the files:
And why not simply have ed print the result to stdout first? Use ,p
instead of w
:
$ printf '/style\n.,/style/d\n.r red.css\n,p\n' | ed -s $file
- Edited
JuvenalUrbino And why not simply have ed print the result to stdout first?
To be on the safer side, could be hard to eyeball unwanted changed lines without the visual aid of +/- that diff provides, especially when working with larger files since ,p
would print the entire file as opposed to just the edited block of text.
Though, your point made me realize that writing to stdout and piping to diff would be a cleaner solution instead of writing to /tmp (I have a bad habit of writing a lot of /tmp files for short time backup)
Cleaner solution:
$ printf '/style\n.,/style/d\n-r red.css\n,p\n' | ed -s index.html | diff -bu index.html -
- Edited
UPDATE: there was a small typo on my first post (fixed it), replaced the dot with a hyphen: .r red.css
to -r red.css