The new popup menu looks like this:
Here are the relevant lines from my .vimrc
file:
set wildchar=<Tab>
set wildmenu
if v:version >= 900
" Set popup menu
set wildoptions=pum
endif
set wildmode=longest:full,full
set wildignore=*~,#*#,*.sw?,*.o,*.class,.viminfo,*.pdf,*.mp3,*.gz,*.tgz,*.gif,*.jpg,*.png
The new thing is:
if v:version >= 900
" Set popup menu
set wildoptions=pum
endif
I check the vim version because not everyone machine I work on is using vim 9 yet.
]]>#shell
channel in the
Chicago Tech Slack
is what people have as shell aliases. Here are some handy aliases from my
aliases.sh
that runs at shell startup.
ls
Most unixes have an ls
for a short version of the ls
command, and then
ll
for ls -al
. Here’s my version of that.
First, the color options are different on macOS ls
than Linux ls
, so
there are two different aliases depending on OS. The -Fp
options tell
ls
to print symbols as annotations for the names listed in the output.
if [ "${OSTYPE:0:6}" == 'darwin' ] ; then
# Mac has -G for color.
alias ls='ls -GFp'
alias ll='ls -GFp -l'
else
alias ls='ls -Fp --color=auto'
alias ll='ls -Fp --color=auto -l'
fi
Then, if I’m on a machine that has exa
installed, use that instead.
exa
is an enhanced version of ls
, but I
don’t have it installed everywhere.
# If we have exa, update ll to use it instead.
hash exa 2>/dev/null && alias ll='exa -F -l -B --git'
I work with both Git and Subversion throughout the day, so I have aliases for both.
# Show me status of files, but not files Subversion doesn't recognize.
alias ssq='svn status -q'
# Show what was changed in the most recent commit.
alias sdrp='svn diff -rPREV'
# Show the log entry for the most recent commit.
alias slrp='svn log -rPREV:COMMITTED'
# Git version of "ssq" above.
alias gsq='git status -sb'
alias gpr='git pull --rebase --stat'
Ed Silva gave me this long ago. I keep
it around because ps
’s options are the worst to remember.
alias memusage='ps -o rss,command -waxc | sort -n'
Speaking of ps
and running progresses, I originally had an alias called
paga
that ran ps aux | grep alester
, but now it’s expanded to do a lot
more, and not care what user I’m logged in as.
alias paga='ps -u "$USER" f --header -o pid,ppid,start_time,%cpu,rssize=Resident,vsize=Virtual,cmd'
The output from paga
shows only my running processes and in a tree format.
PID PPID START %CPU Resident Virtual CMD
18022 18020 21:44 0.0 2732 155040 sshd: andy@pts/0,pts/1
18023 18022 21:44 0.0 6092 119324 \_ -bash
18726 18023 22:07 0.0 1612 155368 | \_ ps -u andy f --header -o pid,ppid,start_time,%cpu,rssize=Resident,vsize=Virtual,cmd
18441 18022 22:05 0.0 6112 119324 \_ -bash
18662 18441 22:07 0.7 8356 152236 \_ vim .bashrc
18724 18662 22:07 0.0 1200 113184 \_ /bin/bash -c (find /) >/tmp/v0sA0Oq/11 2>&1
18725 18724 22:07 52.5 1828 120664 \_ find /
up
is only three characters shorter than cd ..
, but I don’t even want to think about it.
alias up='cd ..'
alias upup='cd ../..'
I use :e
in vim to open a new file, but sometimes I forget I’m in the
shell, and use :e filename
when I mean to use vim filename
. This alias
makes it work anyway.
alias ':e'=vim
I use vimdiff
to show file differences all the time, but sometimes I need
it to ignore whitespace. This is how.
alias vimdiffx='vimdiff -c "set diffopt+=iwhite"'
If I’m on public wifi, I set up a tunnel to my dev machine that my web browser can proxy through. I run this tunnel command and tell my browser to use port 8080 on localhost as a SOCKS5 proxy.
alias cliffordtunnel='ssh -N -f -D8080 tunnel@clifford.petdance.com'
I often want to contact a website and see the HTTP headers, without the content. This command does it.
alias headerdump='curl -D- -o/dev/null'
Are any of these helpful? Should I list more about my shell setup? Let me know at andy@petdance.com.
]]>The error message ack displays when the regex passed is invalid has been improved. The message is more readable and includes a pointer to the offending part of the regex. For example:
$ ack 'status: (open|closed|in progress'
ack: Invalid regex 'status: (open|closed|in progress'
Regex: status: (open|closed|in progress
^---HERE Unmatched ( in regex
ack v3.3.0 also added new file and directory exclusions to speed up file selection.
*.pyc
, *.pyd
and *.pyo
compiled files__pycache__
and .pytest_cache
directories*.so
shared object files*.dll
dynamic-link library files*.mo
compiled translation files__MACOSX
directories and .DS_Store
filesFor a complete list of changes, see the ack 3 changelog.
]]>Probably the biggest influence of functional is that I try to call as few verbs as possible. For example, here’s some code from my codebase at work.
my @got;
push(@got, 'books') if ($list->bwanted > 0);
push(@got, 'eBooks') if ($list->ewanted > 0 and $user->can_order_ebooks eq 'Y');
push(@got, 'A/V items') if ($list->awanted > 0);
That’s three different calls to push
in three lines of code that are very
similar to each other. We shouldn’t need to do that.
The pattern here is that we push the string on the left into @got
if the
condition on the right is true. I created a function called map_x_if_y
to handle this, letting me rewrite the lines above as:
my @got = map_x_if_y(
'books' => ($list->bwanted > 0),
'eBooks' => ($list->ewanted > 0 and $user->can_order_ebooks eq 'Y'),
'A/V items' => ($list->awanted > 0),
);
Now I have no instances of push
, and I’m just declaring conditions and
results. Any work being done is hidden inside a function. I’m only
setting the contents of @got
one time, and the focus is on the data.
The function itself is very simple:
use List::Util qw( pairmap );
sub map_x_if_y {
return pairmap { $b ? ($a) : () } @_
}
I’m not very happy with the name map_x_if_y
. Please email me if you have
ideas for a better name, or if there’s a standard name for this operation.
I was happy with this refactoring, but then it got better. When I ran the
perlcritic
code analyzer
on the original .pm file, I was helpfully informed that “@got is declared
but not used at line…” The only code that’s better than cleaned-up code
is code that I can throw away!
I got the new message because I had the
Variables::ProhibitUnusedVarsStricter
policy installed, and since there was now only one place where @got
was
being referenced, the policy knew that it was not being used.
Another win for thinking functional, and another win for running
perlcritic
often.
ssh
or
scp
commands.
With tab completion in place, I can just type:
$ ssh <tab>
And be presented with a list of known hosts in my ~/.ssh/known_hosts
file, or any hosts in my ~/.ssh/config
file.
$ ssh <tab>
alex
alex.petdance.com
birdie
bogey
bonilla
cacti
champ
etc etc etc
Like all tab completion, I can just type the first letter and <tab> and get a shortened list:
$ ssh g<tab>
git.petdance.com github.com gitlab.com
I had found an ssh
autocompletion script long ago, but it only took
hostnames from ~/.ssh/known_hosts
, and I wanted it to also include host
aliases from my ~/.ssh/config
file. I added the config file parsing, had
a little fight with the compgen
command, but now have it working.
Here’s the magic I had to add to my bash startup.
__complete_ssh_host() {
local KNOWN_FILE=~/.ssh/known_hosts
if [ -r $KNOWN_FILE ] ; then
local KNOWN_LIST=`cut -f 1 -d ' ' $KNOWN_FILE | cut -f 1 -d ',' | grep -v '^[0-9[]'`
fi
local CONFIG_FILE=~/.ssh/config
if [ -r $CONFIG_FILE ] ; then
local CONFIG_LIST=`awk '/^Host [A-Za-z]+/ {print $2}' $CONFIG_FILE`
fi
local PARTIAL_WORD="${COMP_WORDS[COMP_CWORD]}";
COMPREPLY=( $(compgen -W "$KNOWN_LIST$IFS$CONFIG_LIST" -- "$PARTIAL_WORD") )
return 0
}
complete -F __complete_ssh_host ssh
complete -f -F __complete_ssh_host scp
The first complete
line tells bash that whenever I type the ssh
command
and hit <tab> bash should run the function __complete_ssh_host
to
get a list of possible hosts to present as autocomplete targets.
The __complete_ssh_host
looks in the ~/.ssh/known_hosts
file to extract
all the non-IP hostnames it can find, and then looks in ~/.ssh/config
for
hostnames. Then, it passes those hostnames and the partial word you’ve
typed so far to the compgen
command.
compgen
takes a list of things that you could autocomplete, plus whatever
partial word you’ve already typed, and returns a list of valid
autocompletions. Try it from the shell prompt.
$ compgen -W 'aardvark apple baseball boogie cat' -- a
aardvark
apple
Here, I’m supplying a list of words, and then after the --
, the start of
a word, the letter a
. compgen
replies that the options that are
possible are aardvark
and apple
.
This compgen
was confusing, but now that I’ve figured out how to use it
correctly, it makes perfect sense. I wish that the bash manual had an
illustrative example like this one, which would have made everything
immediately clear. I hope that my example here will make it clear for
someone else.
So that’s what I did. My bash prompt already has the hostname in the prompt, so I updated it to give it a different color for each hostname, based on hashing the hostname. It looks like this.
Now, each of the three hosts parker
, clifford
and alex
have a
different color, which can help give a visual cue if something’s wrong.
I’m a big believer in using color to help in orienting. The best demonstration I had was when I accidentally walked into the women’s bathroom at work. I just stood there in the doorway thinking “Wait, what’s wrong?” I couldn’t identify it for a few seconds, but I knew something was wrong. Then it clicked: The wallpaper was a different color than I was used to.
Here’s how I did the magic with the prompt.
local EXIT="$?" # Stash the exit status for later.
# List of color variables that bash can use
local BLACK="\[\033[0;30m\]" # Black
local DGREY="\[\033[1;30m\]" # Dark Gray
local RED="\[\033[0;31m\]" # Red
local LRED="\[\033[1;31m\]" # Light Red
local GREEN="\[\033[0;32m\]" # Green
local LGREEN="\[\033[1;32m\]" # Light Green
local YELLOW="\[\033[0;33m\]" # Yellow
local LYELLOW="\[\033[1;33m\]" # Light Yellow
local BLUE="\[\033[0;34m\]" # Blue
local LBLUE="\[\033[1;34m\]" # Light Blue
local PURPLE="\[\033[0;35m\]" # Purple
local LPURPLE="\[\033[1;35m\]" # Light Purple
local CYAN="\[\033[0;36m\]" # Cyan
local LCYAN="\[\033[1;36m\]" # Light Cyan
local LGREY="\[\033[0;37m\]" # Light Gray
local WHITE="\[\033[1;37m\]" # White
local RESET="\[\033[0m\]" # Color reset
local BOLD="\[\033[;1m\]" # Bold
# Base prompt
local CNAME=$(hostname -s)
local CNAME_MD5
if [ "${OSTYPE:0:6}" == 'darwin' ] ; then
CNAME_MD5=$(hostname -s | md5)
else
CNAME_MD5=$(hostname -s | md5sum)
fi
local RDEC=$((16#${CNAME_MD5:0:2}))
local GDEC=$((16#${CNAME_MD5:2:2}))
local BDEC=$((16#${CNAME_MD5:4:2}))
PS1="\[\e[38;2;${RDEC};${GDEC};${BDEC}m\]$CNAME:$YELLOW\w$CYAN "
if [ "$EXIT" != 0 ]; then
PS1+="${LRED}\\\$ $RESET"
else
PS1+="${LGREEN}\\\$ $RESET"
fi
There’s other stuff in my prompt setup to show the status of the Subversion or Git project I’m working on, too.
I hope someone finds this useful. Drop me an email at andy@petdance.com with any suggestions on improvements.
]]>For example, if you wanted to search for all occurrences of “google” in
HTML files, but only in the <head>
part of the file, you’d use:
ack google --html --range-start='<head>' --range-end='</head>'
There can be more than one range in each file. For example, if your PHP
functions all look like this, with function
and the closing brace always
flush left:
function nuke() {
print "launching nukes";
}
then you could search for only those occurences of “print” within a function like this:
ack print --php --range-start='^function ' --range-end='^}'
For more, see “Matching in a range of lines” in the ack manual.
See the ack install page for where to get and how to install ack.
My thanks to those who helped with this release, including Bill Ricker, Rob Hoelz, Shawn Carroll and Salomon Smeke.
]]>This list is captured from an archive of the original. Thanks, Wayback Machine!
I’ve been doing some work on a not-very-clean codebase. It’s in PHP, but it’s not PHP’s fault that it’s bad. Here are some of the gems from it:
$spaces=" ";
I wonder how many there are there. Who wants to count?
Apparently there was a problem with unescaped ampersands, so here’s the fix:
if ($category == "R&D (Research & Development")
$category = "R&D (Research & Development";
That’s it. One instance of escaping the &. No generalized solution for the next time a variable has an ampersand in it.
Don’t you hate it when you get a message like “You have 1 item(s) in your cart”? Me, too, but this isn’t the way around it.
if ($n > 1)
echo " * - These items will incur a Special Cost of $5.95 (1 item), $8.95 (2 items), or $10.95 (3 or more items) per item<p>";
else
echo " * - This item will incur a Special Cost of $5.95 (1 item), $8.95 (2 items), or $10.95 (3 or more items) per item<p>";
Mattoon is a city in downstate Illinois, and one of our customers is there. Never mind that each special customer gets their own variable flag, like $mattoon
, $poughkeepsie
, etc. Never mind that $mattoon
is a global. They’re just always out of luck here:
$mattoon=0;
if(strtolower($state)=="il" && strtolower(substr($cust_name,0,7)) == "mattoon")
{
$mattoon=1;
}
$mattoon=0;
I’ve written before that the world’s worst variable name is $data
and $data2
, but intentional misspellings to differentiate variables might be up there:
$agencee=$agency;
if ($agencee == "")
$agencee = $agencyid;
Truncate the string first, then strcat
to the end of it.
UserList[0]='\0';
strcat(UserList, userinfo);
Most of us call that strcpy
.
About the only thing worse than intentionally misspelling identifiers to differentiate between similar ones is differentiating by an embedded underline:
sort infile.txt -oin_file.txt +1 -10
Apparently there was some concern that the string might lengthen in midloop in this chunk of C code?
for (z=0;z<strlen(xtitle);z++)
xtitle[z]=toupper(xtitle[z]);
I guess that C’s built-in memset() didn’t fit the bill:
memcpy(ptr,"\0\0\0\0\0\0\0\0\0\0",10);
To add a salesperson to the system, you have to create records in two auxiliary tables. I asked the programmer what had to be done to get this to happen, or if there was a script to do it. His reply:
This is not automated. The SQL commands would be different for each sales rep unless they happened to have the same name and territory.
I asked the programmer about how an update process runs:
Why are the update programs run as root? Was there a permissions problem that you had to get around?
His answer:
When running with root permissions, there are no permission problems. That is why it is run that way.
Jeff Goff notes in IRC: “No permission problems and all permission problems at the same time. It’s kind of like Zen.”
Liz Cortell points out that “That’s the Unix equivalent of ‘I have no drinking problem. I drink. I get drunk. I fall down. No problem’”
The HTML is atrocious, of course. Lots of unnecessary tables.
Best of all, he omits tags that aren’t strictly necessary to browser rendering, such as </td>
tags in tables, and </font>
tags inside those cells.
Here’s some creative indentation. Read carefully!
if ( $condition ) ; else {
do_something();
}
If you need to add commas to a long number, and it’s never occurred to you to use the built-in PHP function number_format
, go ahead and write your own. No need for a generic algorithm, though!
function comedit($fld) {
$fld=(float)$fld; $vao=sprintf("%d",$fld);
$sl=strlen($vao);
if ($sl == 9)
$vao=substr($vao,0,3) . "," . substr($vao,3,3) . "," . substr($vao,6);
elseif ($sl == 8) $vao=substr($vao,0,2) . "," . substr($vao,2,3) . "," . substr($vao,5);
elseif ($sl == 7)
$vao=substr($vao,0,1) . "," . substr($vao,1,3) . "," . substr($vao,4);
elseif ($sl == 6)
$vao=substr($vao,0,3) . "," . substr($vao,3);
elseif ($sl == 5) $vao=substr($vao,0,2) . "," . substr($vao,2);
elseif ($sl == 4)
$vao=substr($vao,0,1) . "," . substr($vao,1);
return($vao);
}
So many choices about how to set the background color!
if ($bgct == 2) {
$bgct=0;
$bgcolor="#D7D7D7";
}
else
$bgcolor="#FFFFFF";
$bgcolor="#FFFFFF";
$bgcolor="#FCF5E4";
For God’s sake, don’t use any more variables than absolutely necessary!
if ($uselc > 'A')
printf("<input TYPE=\"radio\" onclick=\"arrows2(%d);document.simsearch.searchwords.focus()\" NAME=\"sorder\" value=\"4\" $schk4> </td>\n<td><font face=\"arial\" size=\"-1\">LC Call, Author, Title</td>",$imageval+8+$imageadd);
else
printf("<input TYPE=\"radio\" onclick=\"arrows2(%d);document.simsearch.searchwords.focus()\" NAME=\"sorder\" value=\"4\" $schk4> </td>\n<td><font face=\"arial\" size=\"-1\">Dewey, Author, Title</td>",$imageval+8+$imageadd);
This determination of odd numbers, it is too baffling!
for ($x=1;$x<13;$x++) {
if (($x == 1) || ($x == 3) || ($x == 5) || ($x == 7) || ($x == 9) || ($x == 11)) {
....
}
Here’s another way of distinguishing similar variables: Capitalize them!
if ($pages > 0)
$Pages=", $pages" . "p";
else
$Pages="";
This just sort of sums it up:
if ( $month < 1 || $month > 12 ) {
die( "Month must be between 1 and 12\n" );
}
--$month; // Just because that's the way it's always been
A logical OR operator||
would make this simpler.
if ($pubmonth == "")
$pubmonth = 0;
if ($pubmonth == 0)
$pubmonth = 1;
Who needs arrays?
$s0=$s1=$s2=$s3=$s4=$s5=$s6=$s7=$s8=$s9=$s10=$s11=$s12=$s13=$s14=$s15=$s16=$s17=$s18=$s19=$s20=$s21=$s22=$s23=$s24="";
Optimizing cut & pasted code to remove the variable that will always be 10 is a good start, but why only go halfway there?
<textarea name="text1" readonly style="overflow: hidden" rows="<?php print 10 ?>" cols="34" wrap="soft">
One of the biggest problems programmers seem to have is expecting that others know what they’re thinking.
$dformat='l'; // changed on 9-6-01
Changed how? What was $dformat
before? WHY was it changed?
C programming can be so difficult; for example, to open a file with a constant name:
char BookName[50],Review[50];
/* later */
sprintf(Review,"/web/flr.data/fixedbook.idx");
if((Rev=fopen(Review,"a")) == NULL) {
/* ... */
If only there was a way to say, “do this, else do that”…
<?
if ($subuser != 'Y') : ?><a href="...">...</a><?
endif;
if ($subuser == 'Y') : ?><img
src="..."><?
endif;
?>
Bonus: PHP’s awful if (condition) : statements; endif;
syntax!
Make sure that this 100-hyphen line is left-justified in a 100-column space!
printf("%-100s\n",str_repeat("-",100));
Just to be extra sure…
if ($totqty == 0)
$totqty=0;
if ($totprice == 0)
$totprice = 0;
if ($totdisc == 0)
$totdisc = 0;
What’s our record number?
rn=1;
rn = (rn - 1) * rl + lh;
Never mind the reuse of rn
as a variable name, that reduces to:
rn = lh;
I guess he didn’t know that sprintf
can handle more than one variable.
$xxx=sprintf("%02d",$gridrows);
$yyy=sprintf("%02d",$gridcols);
$gridrepeat=$xxx . $yyy;
If you’ve had problems with memory overruns, the answer is to solve the problem, not throw in arbitrarily huge memory buffers that are actually buffers against slop.
char crazeee1 [100000];
PGresult *lres;
char crazee1 [100000];
PGresult *res2;
char crazee [100000];
PGresult *resthree;
char crazee2 [100000];
PGresult *res4;
When erasing disk drives, you want to write out one pattern on the drive, then write a different pattern over that. But you don’t have to do that with variables.
$isbns="";
$isbns=array();
I’ve never liked the removal of vowels as a method to shorten variables, such as the common exercise here of the results of a query being called $rslt
. But what if you need another one? You call it $rlst
!
Pete Krawczyk, coworker at the time, counted all the variations in the codebase:
4 $r1lt
4 $r2lt
18 $r9lt
3 $ralt
94 $rblt
33 $rclt
29 $rdlt
6 $rest
161 $revt
50 $rflt
608 $rglt
36 $rhlt
20 $rilt
46 $rjlt
188 $rklt
4 $rllt
7 $rlst
53 $rplt
507 $rqlt
66 $rrlt
7345 $rslt
94 $rtlt
32 $rult
169 $rvlt
114 $rwlt
1962 $rxlt
4 $rylt
744 $rzlt
The version 3.0.0 release provides a number of new changes and capabilities.
--proximate
option groups matches near each otherA new option --proximate=N
groups together lines of
output that are within N
lines of each other in the file. This is
useful when looking for matches that are related to each other.
For example, these results:
15: First match
18: Second match
19: Third match
37: Fourth match
look like this with --proximate=1
.
15: First match
18: Second match
19: Third match
37: Fourth match
-w
option for word matchingThe -w
option, which tells ack to only find whole-word
matches, did not always work if your pattern began or ended with
puncutation. ack would make guesses as to what your intent was,
but it was not well-defined. Now, ack disallows regexes that
begin or end with non-word characters.
This means that if you try ack -w foo:
, the new ack
will not allow it because :
is not a word character.
-S
as a synonym for --smart-case
For those without --smart-case
always on, the -S
will be easier for when you do want to use it.
Smart-case matching makes ack do a case-insensitive search unless the pattern being matched has a capital letter in it.
-I
to force case-sensitivityIf you use --smart-case
in your .ackrc configuration file, you can use
-I
to force case-sensitivity instead of having to use
--no-smart-case
(which still works).
Run times for ack 3 compared to ack 2.22 are 30-40% faster because of removal of unused infrastructure for plugins.
ack now supports SVG, Markdown and POD by default.
Column numbers were not getting colorized in the output. Added
--color-colno
option and ACK_COLOR_COLNO
environment variable.
A pattern that wanted whitespace at the end could match the linefeed at the end of a line. This is no longer possible.
ack 2 only needed Perl 5.8.8. This shouldn’t be a problem since 5.10.1 has been out since 2009.
ack 3 will not allow you to use -w
with a pattern that doesn’t begin or
end with a word character.
ack 2 would highlight your capture groups. For example,
ack '(set|get)_foo_(name|id)'
would highlight the set
or get
, and the name
or id
, but not the
full set_user_id
that was matched.
This feature was too confusing and has been removed. Now, the entire matching string is highlighted.
In ack 2, you could put any kind of Perl code in the --output
option and it would get eval
uated at run time, which would let
you do tricky stuff like this gem from Mark Fowler
(http://www.perladvent.org/2014/2014-12-21.html):
ack --output='$&: @{[ eval "use LWP::Simple; 1" && length LWP::Simple::get($&) ]} bytes' \
'https?://\S+' list.txt
http://google.com/: 19529 bytes
http://metacpan.org/: 7560 bytes
http://www.perladvent.org/: 5562 bytes
This has been a security problem in the past, and so in ack 3 we
no longer eval
the contents of --output
. You’re now restricted
to the following variables: $1
thru $9
, $_
, $.
, $&
, $`
,
$'
and $+
. You can also embed \t
, \n
and \r
,
and $f
as stand-in for $filename
in ack2 --output
.
ACK_OPTIONS
environment variableThe ACK_OPTIONS
variable was used to supply command line arguments to
ack invocations. This has been removed. Use an ackrc file instead.
--lines
optionThe --lines
option let you select a specific range of lines to print, and
not match. There are other better ways to do this (sed
, for example).
Thanks to everyone who helped contribute to this new release, including Bill Ricker, Rob Hoelz, Slaven Rezić, Shlomi Fish, Axel Beckert and Mitch Lacy.
]]>--help-colors
and
--help-rgb-colors
.
The --help-colors
option shows a chart of various color combinations using the standard eight ANSI terminal colors.
The --help-rgb-colors
option shows a grid of the 216 colors possible using RGB color specifications.
Thanks to Bill Ricker for his work in getting the RGB color chart organized in a useful way, via the Convert::Color module.
ack is available on the CPAN, and also as a single-file download at beyondgrep.com.
Let me know if you find it useful.
]]>