Fd: Using fd, --exec, and cd

Created on 8 Aug 2019  路  3Comments  路  Source: sharkdp/fd

All, this might not be even possible or advisable with fd but I'm trying to learn how to be more of a master. I pretty much know how I can script this in bash with find, etc. but fd is better and powerful and I want to learn.

Namely, let's say I have a set of git repositories in a folder. They aren't subrepos or anything, just separate repos. I want to use fd to go into each repo run git diff and then come out. Essentially see all the diffs from below. Now, I can find all the git dirs with:

fd -H --no-ignore-vcs -t d .git$

(The --no-ignore-vcs is something I just always use for other reasons and I'm too lazy to alias it and I don't think there is a .fdrc file a la .ripgreprc?)

Running that command in a sample directory I see:

Applications/@CPLFCST_Etc/.git
Applications/@GEOSgcm_App/.git
Applications/@UMD_Etc/.git
Components/@GEOSgcm_GridComp/.git
Components/@GEOSgcm_GridComp/GEOSagcm_GridComp/GEOSphysics_GridComp/@GEOSchem_GridComp/.git
Components/@GEOSgcm_GridComp/GEOSogcm_GridComp/GEOSocean_GridComp/GuestOcean_GridComp/MOM_GEOS5PlugMod/@mom/.git
Shared/@GMAO_Shared/.git
Shared/@MAPL/.git
Shared/@NCEP_Shared/.git

So reading this from the front page README:

{//}: Uses the parent of the discovered path (documents/images).

I thought, oh, that can give me a path without .git, but I tried the naive thing and:

$ fd -H --no-ignore-vcs -t d .git$ --exec cd {//} && git diff
[fd error]: Command not found: "cd" "//" "Applications/@CPLFCST_Etc/.git"
[fd error]: Command not found: "cd" "//" "Applications/@GEOSgcm_App/.git"
[fd error]: Command not found: "cd" "//" "Applications/@UMD_Etc/.git"
[fd error]: Command not found: "cd" "//" "Shared/@MAPL/.git"
[fd error]: Command not found: "cd" "//" "Components/@GEOSgcm_GridComp/.git"
[fd error]: Command not found: "cd" "//" "Shared/@GMAO_Shared/.git"
[fd error]: Command not found: "cd" "//" "Shared/@NCEP_Shared/.git"
[fd error]: Command not found: "cd" "//" "Components/@GEOSgcm_GridComp/GEOSagcm_GridComp/GEOSphysics_GridComp/@GEOSchem_GridComp/.git"
[fd error]: Command not found: "cd" "//" "Components/@GEOSgcm_GridComp/GEOSogcm_GridComp/GEOSocean_GridComp/GuestOcean_GridComp/MOM_GEOS5PlugMod/@mom/.git"

So I obviously don't know what I'm doing. Help?

question

Most helpful comment

Hi,

There are multiple issues here. Most of them are related to proper escaping. Shells have a large number of special characters (e.g. $, {, ., &&) that all need escaping.

I would start by quoting the search pattern. It may work in this example, but it isn't guaranteed to always work if you don't escape it:

fd -H -td '.git$'

While we are at it, let's also fix the search pattern. .git$ searches for anything that ends with "[any character]git" (for example: "digit"). I think you rather want:

fd -H -td '^\.git$'

Next, the --exec part. If you run

fd -H --no-ignore-vcs -t d .git$ --exec cd {//} && git diff

your shell will see (added newlines to make the operator precedence clear):
fd -H --no-ignore-vcs -t d .git$ --exec cd {//}
&&
git diff

So it actually runs fd -H --no-ignore-vcs -t d .git$ --exec cd {//} first and would run git diff if fd would succeed. fd doesn't even get to see the && git diff part.

The next problem is that fd --exec does not take a shell command line as an argument, but rather a proper program invocation. Note that cd is not a proper command. It's a shell builtin. This is why you see the "command not found" error message.

You could actually run a shell command with fd --exec by using fd --exec bash -c "...", but I think there is a better solution here.

git has a -C <path> option that let's you run git as if it had been started from <path>. So I think this should work (can not test right now):

fd -H -td '^\.git$' -x git -C '{//}' diff

As a tip for the future: I often run fd ... --exec echo ... first to check what is actually executed.

All 3 comments

Hi,

There are multiple issues here. Most of them are related to proper escaping. Shells have a large number of special characters (e.g. $, {, ., &&) that all need escaping.

I would start by quoting the search pattern. It may work in this example, but it isn't guaranteed to always work if you don't escape it:

fd -H -td '.git$'

While we are at it, let's also fix the search pattern. .git$ searches for anything that ends with "[any character]git" (for example: "digit"). I think you rather want:

fd -H -td '^\.git$'

Next, the --exec part. If you run

fd -H --no-ignore-vcs -t d .git$ --exec cd {//} && git diff

your shell will see (added newlines to make the operator precedence clear):
fd -H --no-ignore-vcs -t d .git$ --exec cd {//}
&&
git diff

So it actually runs fd -H --no-ignore-vcs -t d .git$ --exec cd {//} first and would run git diff if fd would succeed. fd doesn't even get to see the && git diff part.

The next problem is that fd --exec does not take a shell command line as an argument, but rather a proper program invocation. Note that cd is not a proper command. It's a shell builtin. This is why you see the "command not found" error message.

You could actually run a shell command with fd --exec by using fd --exec bash -c "...", but I think there is a better solution here.

git has a -C <path> option that let's you run git as if it had been started from <path>. So I think this should work (can not test right now):

fd -H -td '^\.git$' -x git -C '{//}' diff

As a tip for the future: I often run fd ... --exec echo ... first to check what is actually executed.

@mathomp4 A short update from your side would be appreciated.

@mathomp4 A short update from your side would be appreciated.

Whoops. Sorry about this. I thought I did update this thread. It works great and I'm using it in my script now. (And using the echo bit quite a bit!)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

matu3ba picture matu3ba  路  4Comments

sharkdp picture sharkdp  路  3Comments

blueray453 picture blueray453  路  3Comments

mwgkgk picture mwgkgk  路  3Comments

christianbundy picture christianbundy  路  3Comments