How to use a parenthesis character inside a command substitution inside a bash snippet inside a git configuration alias?

Here is an excerpt from the config I am editing. This is inside the [alias] section of my .gitconfig:

ignored = !git ls-files -v | grep "^S"
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"

I am basically trying to colorize the output of git ignored as I stick it onto the end of the git status. What I show above works fine, but it does not work (fatal: bad config file line 18 in ~/.gitconfig) if my sed command has inside it any parentheses (which are quite useful for building regexes).

  • Python 3 Wont run from the Git Bash command line
  • Can't store sh command output through DSL (groovy) in Jenkins pipeline job
  • Excluding the arrow pattern “->” using grep
  • How to see changed lines with certain words and the containing file for a git commit? - Can git diff print a file name line prefix?
  • Bash scripting, git and filenames with spaces
  • Shell script - trying to validate if a git tag exists in a git repository in an if/else statement
  • For example, I want to be able to write something having parens, such as sed s/^(.*)$/z\1z/ instead of sed -e s/^/z/ -e s/$/z/.

    Since i’m colorizing stuff I would actually be using \x1b[31m, \x1b[m, etc., but you get the idea.

    I have tried one, two, three, and four backslashes to escape these parens but nothing works.

  • git rebase with conflict not work
  • How to include modified configuration files in a docker image?
  • How to add image to readme file in Stash
  • What is the best way to generate an svn compatible diff when using git svn?
  • Clone multiple SVN projects with git-svn
  • Submit patch to Github project without cloning repository to Github?
  • One Solution collect form web for “How to use a parenthesis character inside a command substitution inside a bash snippet inside a git configuration alias?”

    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.

    Git Baby is a git and github fan, let's start git clone.