3

I want to write bash scripts that are as self-documentary as possible.

I had an idea that when I execute software with lots of flags, I wanted to split the command into multiple lines, and add a comment at the end of each line, telling what the flag does (info from a man page), like so:

bwa aln \
-n $n \ # -n max #diff (integer) or missing prob under 0.02 err rate (float) [0.04]
-o $o \ # -o maximum number or fraction of gap opens [1]
-e $e \ # -e maximum number of gap extensions, -1 for disabling long gaps [-1]
-i $i \ # -i do not put an indel within integer bp towards the ends [5]
-d $d \ # -d maximum occurrences for extending a long deletion [10]
-l $l \ # -l seed length [32]
-k $k \ # -k maximum differences in the seed [2]
-m $m \ # -m maximum entries in the queue [2000000]
-t $t \ # -t number of threads [1]
-M $M \ # -M mismatch penalty [3]
-O $O \ # -O gap open penalty [11]
-E $E \ # -E gap extension penalty [4]
-R $R \ # -R stop searching when there are >integer equally best hits [30]
-q $q \ # -q quality threshold for read trimming down to 35bp [0]
-f $f \ # -f file to write output to instead of stdout
-B $B \ # -B length of barcode
-L $L \ # -L log-scaled gap penalty for long deletions
-N $N \ # -N non-iterative mode: search for all n-difference hits (slooow)
-I $I \ # -I the input is in the Illumina 1.3+ FASTQ-like format
-b $b \ # -b the input read file is in the BAM format
-0 $0 \ # -0 use single-end reads only (effective with -b)
-1 $1 \ # -1 use the 1st read in a pair (effective with -b)
-2 $2 \ # -2 use the 2nd read in a pair (effective with -b)
-Y $Y \ # -Y filter Casava-filtered sequences
-prefix $prefix \ # -prefix Prefix
-inputfile $inputfile \ # -inputfile Input file (FastQ format)

The problem is that I can not have anything after the \ character (that tells bash that the command continues on next line), and neither can I have the "\" at the end of the line, because then it is treated as part of the comment.

Anybody knows a way to do this, or something similar?

2
  • 1
    Makes it very hard to read! What's wrong with having multi-line comments before the command?
    – gawbul
    Commented Sep 6, 2013 at 18:58
  • I have a quite specific use case here, where I want to show how a command is built up (I maybe have to make a video of this :) ): As you select things in a wizard, the "$[var]" part gets replaced. I don't have much screen estate for the command, so making it double as high, by putting the list above, will be too much. I also find it very helpful, when you have that many command line options, to have the comment absolutely adjacent to where it is set. Commented Sep 7, 2013 at 6:59

4 Answers 4

3

You can evaluate comments before the slash, simply generating empty strings for comments which won't affect your script:

bwa aln \
-n $n `# -n max #diff (integer) or missing prob under 0.02 err rate (float) [0.04]` \
-o $o `# -o maximum number or fraction of gap opens [1]` \
....
5
  • 1
    that's distracting and doesnt follow standard programming practices for comments, regardless of language. put the comment on its own line. because it inserts a comment by using code you should technically be commenting the additional code which is extra work that could have been avoided by investing in the comments instead.
    – Joshua K
    Commented Sep 6, 2013 at 18:00
  • 6
    I may be wrong, but aren't you effectively spawning a subshell with every 'comment'?
    – Bob
    Commented Sep 6, 2013 at 18:15
  • +GoodTimeTribe: I would disagree. It's like saying Python docstrings are distraction, ... Commented Sep 6, 2013 at 23:55
  • +Bob: Yes it clones the shell that immediately returns. Commented Sep 7, 2013 at 0:20
  • @Soheil – you need to reply with @, not a +
    – slhck
    Commented Sep 7, 2013 at 9:29
6

I suggest using multiple lines to comment the code before the command. like

 # this command uses multiple parameters
 # it requires 4 parameters and none are optional with no defaults
 # parameters used : 
 # -q              name of the file
 # -b              size to truncate
 # -n              new location
 # -r              recursive
2

One option is to construct your command in pieces so you don't have to worry about line continuation:

cmd='date'                # run the date program
cmd=${cmd}' -d 20130905'  #  for this date
cmd=${cmd}' +%s'          #  with output in this format
echo $cmd                 # review the command
eval $cmd                 # run the command
4
  • Works too, although slightly harder to read. Thanks for the tip though! Commented Sep 6, 2013 at 16:56
  • while i kind of like this, because it's flexible and has flow and additional logic, i feel it puts work into coding when it could've just been commented with grace and style instead.
    – Joshua K
    Commented Sep 6, 2013 at 18:02
  • This will not work for any arguments that actually contain spaces. Replace '20130905' with '3 days ago' to see what I mean.
    – chepner
    Commented Sep 9, 2013 at 13:04
  • better create variables for each flag and comment those and not eval imho
    – CervEd
    Commented Oct 6, 2022 at 8:19
0

For me something like this works good enough. Solution focused on building string by concatenation. (Compare release build vs debug build)

#############################################################################
#### GRPC ########################################################## GRPC ###
#############################################################################
# GRPC - depends on protobuf (above)
#
# gRPC_BUILD_CODEGEN=ON - needed to build grpc_cpp_plugin
# ABSL_ENABLE_INSTALL=ON - needed to install Abseil
#
TYPE=RelWithDebInfo
INSTALL=${BUILD_DIR}/install-${TYPE}
BUILD=build-grpc-${TYPE}
echo "Building: $BUILD <-------------------------------------------------------"
(cd ${BUILD_DIR} && rm -rf $BUILD && mkdir -p $BUILD) || exit 1
(cd ${BUILD_DIR}/$BUILD && cmake ${DIR}/../grpc \
    -DgRPC_BUILD_TESTS=OFF \
    -DgRPC_BUILD_CODEGEN=ON \
    -DgRPC_BUILD_CSHARP_EXT=OFF \
    -DgRPC_INSTALL=ON \
    -DgRPC_PROTOBUF_PROVIDER=package \
    -DgRPC_SSL_PROVIDER=package \
    -DgRPC_ZLIB_PROVIDER=package \
    -DgRPC_BUILD_GRPC_CPP_PLUGIN=ON \
    -DgRPC_BUILD_GRPC_PYTHON_PLUGIN=ON \
    -DgRPC_BUILD_GRPC_CSHARP_PLUGIN=OFF \
    -DgRPC_BUILD_GRPC_NODE_PLUGIN=OFF \
    -DgRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN=OFF \
    -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF \
    -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF \
    -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF \
    -DABSL_ENABLE_INSTALL=ON \
    -DBUILD_SHARED_LIBS=OFF \
    -DCMAKE_BUILD_TYPE=${TYPE} \
    -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang \
    -DCMAKE_INSTALL_PREFIX=${INSTALL}) || exit 1
(cd ${BUILD_DIR}/$BUILD && cmake --build . --target install -j $THREADS) || exit 1
(cd ${BUILD_DIR} && rm -rf $BUILD) # Cleanup
#############################################################################
#############################################################################
TYPE=Debug
INSTALL=${BUILD_DIR}/install-${TYPE}
BUILD=build-grpc-${TYPE}
echo "Building: $BUILD <-------------------------------------------------------"
(cd ${BUILD_DIR} && rm -rf $BUILD && mkdir -p $BUILD) || exit 1


GRPC_BUILD_FLAGS="                    -DgRPC_BUILD_TESTS=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_CODEGEN=ON"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_CSHARP_EXT=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_CSHARP_EXT=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_INSTALL=ON"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DABSL_ENABLE_INSTALL=ON"                 # needed to install Abseil
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_PROTOBUF_PROVIDER=package"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_SSL_PROVIDER=package"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_ZLIB_PROVIDER=package"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_GRPC_CPP_PLUGIN=ON"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_GRPC_PYTHON_PLUGIN=ON"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_GRPC_CSHARP_PLUGIN=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_GRPC_NODE_PLUGIN=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DBUILD_SHARED_LIBS=ON"
GRPC_BUILD_FLAGS="${GRPC_BUILD_FLAGS} -DCMAKE_BUILD_TYPE=${TYPE}"


(cd ${BUILD_DIR}/$BUILD && cmake ${DIR}/../grpc \
    ${GRPC_BUILD_FLAGS} \
    -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang \
    -DCMAKE_INSTALL_PREFIX=${INSTALL}) || exit 1
(cd ${BUILD_DIR}/$BUILD && cmake --build . --target install -j $THREADS) || exit 1
(cd ${BUILD_DIR} && rm -rf $BUILD) # Cleanup
#############################################################################

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .