#!/bin/csh -f # ct # ----------------------------------------------------------------------------- # C shell script to pushd to the specified directory, then cd to its Tips # subdirectory, then show the files there. Useful for moving to the Tips # subdirectory of a directory on the cdpath, to operate on that category of # tips. # ----------------------------------------------------------------------------- # Usage: See Usage section below or run with -h or --help to see usage. # Assumptions: # Effects: # Notes: # Implementation Notes: # - This was originally just an alias defined in .cshrc: # alias ct 'pushd \!* && cd ./Tips && ls -FlA' # but && did not properly abort on missing folder. Had to move # to a script instead. Why? # - True on macOS 11.1, but works fine on Linux 3.2.22-35.60.amzn1.x86_64 # - Figured it out. The problem is that pushd and cd are aliases that # contain multiple commands separated by semicolon (";"). # On both macOS and Linux: # - A semicolon ends one command and starts another, and the && only # sees the $status of the last command, not the whole sequence. # - The failed call to the builtin pushd command (from the pushd alias) # aborts the entire command (script, alias, or whatever). # On macOS: # - The abort happens AFTER the rest of the line completes, including # the parts that should have been short circuited. # On Linux: # - The abort happens BEFORE the rest of the line completes, so the # parts that should have been short circuited are skipped by the abort. # Prove it by running show_that_semicolons_in_aliases_break_short_circuits # on Mac and Linux. # --Fred 6/14/2021 #?? - Possible solution is to redefine all of my aliases to use && instead #?? of ; whenever possible. Then this script could also be an alias. #?? That might be a good idea to prevent similar problems in the future, #?? but I may as well leave this script as a script since it's now #?? working as desired. # - This script by itself can't change the current working directory # of the shell that calls it. Have to define an alias in ~/.cshrc # to source it. See Usage below. # Portability Issues: # Revision History: # $Log$ # ----------------------------------------------------------------------------- #?? Can't use $0 in this script. When called via source, $0 is "-tcsh". #?? Have to hardcode the script name for now. Any better ideas? set script_name = "ct" # Collect command line options while ($#argv > 0) if ("$1:q" == "-h" || "$1:q" == "--help") then echo "Usage: $script_name [options] directory_name" echo " where $script_name is an alias defined in .cshrc as:" echo " alias $script_name 'source ~/bin/$script_name'" echo "Options:" echo " -h = Show this help text" echo " --help = Show this help text" exit 1 else if ("$1" != "-" && "-" == "`echo $1:q | cut -c 1`") then echo "Error: Invalid option: $1:q" $script_name --help exit 1 else # Not a recognized option. Assume it's the first parameter break endif end # Collect command line arguments if ($#argv > 1) then echo "Error: Too many arguments" $script_name --help exit 1 else if ($#argv < 1) then echo "Error: Must specify a directory name" $script_name --help exit 1 endif #?? This doesn't work when called via source. #?? pushd $*:q && cd ./Tips && ls -FlA #?? Like the alias described in "Implementation Notes" above, it fails to #?? properly abort on missing folder. Why? #?? - Same reason as above. Caused by ; inside aliases of pushd and cd. #?? Had to re-write with explicit tests of $status instead of using &&. # Note: Use pushd, not cd, so the user can use pop afterwards, once they're # done operating on the Tips. pushd $*:q #?? Have to put cd and ls commands in explicit then blocks. Otherwise, #?? they fail to use the cd and ls aliases. These lines ignore the aliases. #?? Why: #?? if (! $status) cd ./Tips #?? if (! $status) ls -FlA #?? Experiments show that aliases are not used on any one-line if statement, #?? even interactively outside of any script. Seems to be true in tcsh #?? on both macOS 11.1 and Linux 3.2.22-35.60.amzn1.x86_64. Prove it by #?? running show_that_aliases_are_ignored_on_if_lines on both of them. #?? --Fred 6/14/2021 if (! $status) then cd ./Tips else echo "Aborting since pushd failed... endif if (! $status) then ls -FlA else echo "Aborting since cd ./Tips failed... endif