There are separate problems here: One with your config escaping, one with parentheses in sed
, and one with process substitution like <(date)
in git aliases.
Config escaping
Firstly, the problem with bad config file
isn't command substitution, it's your newline character.
[YOURS] status-with-ignored = "!f() { git status; ignored=$(git ignored | cut -d ' ' -f 2 | sed -e s/^/z/ -e s/$/z/); [ -n \"$ignored\" ] && echo \"git skip-worktree (ignored):\n$ignored\"; };f"
[MINE] status-with-ignored = "!f() { git status; ignored=$(git ignored | cut -d ' ' -f 2 | sed -e s/^/z/ -e s/$/z/); [ -n \"$ignored\" ] && echo \"git skip-worktree (ignored):\\n$ignored\"; };f"
It looks like your \n
is being interpreted as applying to the config file; that's the part that needs escaping, not anything paren-related.
I got the above by letting bash
and git config
handle the escaping for me, and then substituting cut's " "
for ' '
:
git config alias.status-with-ignored '!f() { git status; ignored=$(git ignored | cut -d " " -f 2 | sed -e s/^/z/ -e s/$/z/); [ -n "$ignored" ] && echo "git skip-worktree (ignored):\n$ignored"; };f'
(If you want to get clever about escaping single quotes within bash
single quotes, you can, but to me it works actively against readability.)
Parentheses in sed
Secondly, Bash and other shells can't handle unquoted parentheses. That's not specific to git aliases, that's just the parsing rules.
echo I am (Batman) # Doesn't work
echo I am Batman # Does work
The latter one works; Batman has no parens.
You can either escape the parentheses or quote the string:
echo "I am (Batman)" # Works
echo I am \(Batman\) # Also works
Which leaves your config file like this:
status-with-ignored = "!f() { git status; ignored=$(git ignored | cut -d ' ' -f 2 | sed -e \"s/^(.*)$/z\1z/\"); [ -n \"$ignored\" ] && echo \"git skip-worktree (ignored):\\n$ignored\"; };f"
Or your git config
statement like this:
git config alias.status-with-ignored '!f() { git status; ignored=$(git ignored | cut -d " " -f 2 | sed -e "s/^(.*)$/z\1z/"); [ -n "$ignored" ] && echo "git skip-worktree (ignored):\n$ignored"; };
Process substitution in git
Finally, it seems that git alias
just doesn't handle process substitution. From the git config
man page:
Arguments are split by spaces, the usual shell quoting and escaping is supported. A quote pair or a backslash can be used to quote them.
It looks like git is using sh
or its own parsing, and not calling out to bash
itself, so as a non-portable bash extension process substitution isn't supported natively. You could shell out to bash:
git config alias.sample '!cat <(seq 3)'; git sample
git config alias.sample '!bash -c "cat <(seq 3)"'; git sample
...but at that point you might as well just make a script named git-status-with-ignored
and add it to your path.