1
\$\begingroup\$

This is the second iteration of this question

A note to start with: the first iteration of this question can be found here:
Editing system files in Linux (as root) with GUI and CLI text editors


As stated in there:

My intention is to POSIX-ly write one generalized function for running various text editors I use for different purposes through sudoedit, i.e. editing files as root safely. Safely = for instance, if a power loss occurs during the file edit; another example could be lost SSH connection, etc.


I come to you now with a possible final solution:


sudoedit enhanced

# USAGE: Just source this file into your shell for instance if using Bash, then you can use these files: ~/.bashrc or ~/.bash_aliases
# Please, customize these lists to your preference before using this script!
sudoedit__cli_editor_list='nano vi'
sudoedit__gui_editor_list='gedit emacs xed subl' # VS Code has its own work-around
sudoedit_enhanced_run ()
# Generic function for text editing as root
# the proper, safe, way through `sudoedit`.
# Expected arguments:
# 1ドル = editor name; obviously mandatory
# 2ドル = wait option; to avoid this, pass an empty string ('')
# 3,ドル (4ドル), ... = file(s); at least one file must be given
{
 # check the minimum number of arguments
 if [ $# -lt 3 ]; then
 printf '%b\n' "sudoedit_enhanced_run(): Low number of arguments.\\nExpected: \1ドル = editor name; \$2 = wait option; \3,ドル (\$4), ... = file(s).\\nPassed $#: $*" >&2
 return 1
 fi
 # let's take a closer look at the first argument, the editor
 editor_name=1ドル
 # store an editor alias, if there is any
 editor_alias=$( alias "$editor_name" 2> /dev/null )
 # remove that alias for now
 if [ -n "$editor_alias" ]; then
 unalias "$editor_name"
 fi
 # find out if such editor exists on the system
 # and store the first two arguments to variables
 editor_path=$( command -v "$editor_name" 2> /dev/null )
 wait_option=2ドル
 # if that editor does not return valid path, print error and bail
 if ! [ -x "$editor_path" ]; then
 printf '%s\n' "sudoedit_enhanced_run(): This editor ('$editor_name') is not installed on this system." >&2
 return 1
 fi
 # if we got here, then both of the things are ok;
 # so let's move past the editor and its wait option to the actual files
 shift 2
 # check if all the files exist, it does not make sense to create a file this way
 for file in "$@"; do
 if ! [ -f "$file" ]; then
 printf '%s\n' "sudoedit_enhanced_run(): This file ('$file') does not exist or it is not a regular file." >&2
 return 1
 fi
 done
 # run the editor with one-time SUDO_EDITOR set-up
 SUDO_EDITOR="$editor_path $wait_option" sudoedit "$@"
 # re-define the editor alias, if there was any, afterward
 if [ -n "$editor_alias" ]; then
 eval "$editor_alias"
 fi
}
# Editor aliases generators / definitions
for cli_editor in $sudoedit__cli_editor_list; do
 alias su$cli_editor="sudoedit_enhanced_run $cli_editor ''"
done
for gui_editor in $sudoedit__gui_editor_list; do
 alias su$gui_editor="sudoedit_enhanced_run $gui_editor -w"
done
# VS Code specific workaround to work under root
alias sucode="sudo mkdir -p /root/.vscode && sudo code -w --user-data-dir=/root/.vscode"

Already posted on GitHub, whereas I also tried hard to describe the purpose and (in spite it shall be obvious) usage including some example images on the project GitHub page.

asked Feb 2, 2020 at 11:21
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Self-review


comments

Comments are very important to future readers, they speed up the comprehension of the whole code. I think I messed up at least one comment, (others pending review):

  • original:

    # let's take a closer look at the first argument, the editor
    
  • suggested:

    # store the first argument, the editor name
    

combine what can be combined

By combining simple pieces of code, we make it easier to read.

  • original:

    # store an editor alias, if there is any
    editor_alias=$( alias "$editor_name" 2> /dev/null )
    # remove that alias for now
    if [ -n "$editor_alias" ]; then
     unalias "$editor_name"
    fi
    
  • suggested:

    # store an editor alias; and if there is any, remove it for now
    if editor_alias=$( alias "$editor_name" 2> /dev/null ); then
     unalias "$editor_name"
    fi
    

implement code workaround into the function

My previous solution does not do any checks for code and also, by doing this we get rid of that alien alias.

  • original:

    # VS Code specific workaround to work under root
    alias sucode="sudo mkdir -p /root/.vscode && sudo code -w --user-data-dir=/root/.vscode"
    
  • suggested:

    # run the editor with one-time SUDO_EDITOR set-up
    if [ "$editor_name" = code ]; then
     # code specific workaround
     sudo mkdir -p /root/.vscode &&
     sudo code -w --user-data-dir=/root/.vscode "$@"
    else
     # main command generic
     SUDO_EDITOR="$editor_path $wait_option" sudoedit "$@"
    fi
    

avoid generating editors aliases for which editor is not installed

My previous solution is generating all editor aliases, no matter if such program is installed on the system, this could have been unpleasant to users.

  • original:

    for cli_editor in $sudoedit__cli_editor_list; do
     alias su$cli_editor="sudoedit_enhanced_run $cli_editor ''"
    done
    for gui_editor in $sudoedit__gui_editor_list; do
     alias su$gui_editor="sudoedit_enhanced_run $gui_editor -w"
    done
    
  • suggested:

    for cli_editor in $sudoedit__cli_editor_list; do
     if command -v "$cli_editor" > /dev/null 2>&1; then
     alias su$cli_editor="sudoedit_enhanced_run $cli_editor ''"
     fi
    done
    for gui_editor in $sudoedit__gui_editor_list; do
     if command -v "$gui_editor" > /dev/null 2>&1; then
     alias su$gui_editor="sudoedit_enhanced_run $gui_editor -w"
     fi
    done
    
answered Feb 6, 2020 at 11:53
\$\endgroup\$
2
\$\begingroup\$

This long line:

printf '%b\n' "sudoedit_enhanced_run(): Low number of arguments.\\nExpected: \1ドル = editor name; \$2 = wait option; \3,ドル (\$4), ... = file(s).\\nPassed $#: $*" >&2

can easily be made more tractable by separating the lines (since we have \n in the format string) and by using single quotes where we don't want expansion (avoiding the need to write \$):

# shellcheck disable=SC2016
printf '%s\n' >&2 \
 'sudoedit_enhanced_run(): Low number of arguments.' \
 'Expected: 1ドル = editor name; 2ドル = wait option; 3,ドル (4ドル), ... = file(s).' \
 "Passed $#: $*"
answered Feb 6, 2020 at 17:31
\$\endgroup\$
1
  • \$\begingroup\$ It's not actually concatenating them: it's just re-using the same format string for 3 sets of arguments (only one in each set here, of course). It does look strange when you're used to C's printf, but it's really useful. I don't know of a specific name, though. \$\endgroup\$ Commented Feb 7, 2020 at 9:26

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.