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?
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!)
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:
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:Next, the
--execpart. If you runyour shell will see (added newlines to make the operator precedence clear):
fd -H --no-ignore-vcs -t d .git$ --exec cd {//}&&git diffSo it actually runs
fd -H --no-ignore-vcs -t d .git$ --exec cd {//}first and would rungit diffiffdwould succeed.fddoesn't even get to see the&& git diffpart.The next problem is that
fd --execdoes not take a shell command line as an argument, but rather a proper program invocation. Note thatcdis 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 --execby usingfd --exec bash -c "...", but I think there is a better solution here.githas a-C <path>option that let's you rungitas if it had been started from<path>. So I think this should work (can not test right now):As a tip for the future: I often run
fd ... --exec echo ...first to check what is actually executed.