yeti ...alternatively to the rude ctrl-d:

Even better is: at -f script TIME or at TIME < script--the latter is how I generally run it. 😉

  • Jay likes this.
7 days later
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.)

    pfr That echo bit is redundant as date allows arbitrary text in the format string. Try this:

    $ ffmpeg -loglevel fatal -f x11grab -i :0 -frames:v 1 "$(date +"ss-%d-%m-%Y--%H.%M.%S.png")"

    ffmpeg doesn't like : in the output filename.

      @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,

      • rvp replied to this.
      • Jay likes this.

        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!

          a month later
          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)
          3 months later
          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
          $
          2 months later
          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):

          1. 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.

          2. Using cat(1) and here-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:

          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

            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
            
            • rvp replied to this.
            • pfr likes this.

              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<<<
              $
              a month later

              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.

              3 months later

              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
                6 days later

                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

                  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
                    • oui replied to this.

                      JuvenalUrbino

                      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 -

                      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