Reputation: 7366
My goal is to find all files named README.md
in sub-folders, and to copy them in an output
folder, using the name of their original folder as their new name.
Ok that sounds complicated. Let's see a concrete example:
baselines
├── combined_tree_local_conflict_obs
│ ├── README.md
│ └── sparse_small_apex_maxdepth2_spmaxdepth30.yaml
└── global_density_obs
├── README.md
└── sparse_small_apex_expdecay_maxt1000.yaml
I want to copy the README.md
files to the output
folder with the names combined_tree_local_conflict_obs.md
and global_density_obs.md
.
So in the end I'd have:
baselines
├── output
│ ├── combined_tree_local_conflict_obs.md
│ └── global_density_obs.md
├── combined_tree_local_conflict_obs
│ ├── README.md
│ └── sparse_small_apex_maxdepth2_spmaxdepth30.yaml
└── global_density_obs
├── README.md
└── sparse_small_apex_expdecay_maxt1000.yaml
I don't understand why my bash command doesn't work:
$ find baselines -type f -name "README.md" -exec echo output/$(basename $(dirname {})).md \;
output/..md
output/..md
(I'm not copying the files yet but just printing their new path to debug the command.)
The find command does work:
$ find baselines -type f -name "README.md" -exec echo {} \;
baselines/combined_tree_local_conflict_obs/README.md
baselines/global_density_obs/README.md
Extracting the folder name does work:
$ echo $(basename $(dirname "baselines/combined_tree_local_conflict_obs/README.md"))
combined_tree_local_conflict_obs
But! somehow when I put them together it doesn't work.
I'm not so much interested in how to solve this problem in another way, but rather to understand why my command doesn't work.
Upvotes: 0
Views: 368
Reputation: 185126
Using perl, with full regex capabilities to handle the new case of the parent directory baselines
:
perl -MFile::Find -MFile::Copy -we '
find({
no_chdir => 1,
wanted => sub {
if (-f && m/README\.md$/) {
my $mod = my $file = $File::Find::name;
$mod =~ s@.*([^/]+)/.*@output/${1}.md@;
print "$file => $mod\n";
#copy("$file", "$mod") or die $!;
}
}
}, @ARGV);
' ./baselines/
This is in debug mode, to do it for real, remove the print
command and uncomment the copy()
line.
Upvotes: 0
Reputation: 185126
Like Charles Duffy explain in comments,
$(basename $(dirname {}))
runs before find even starts. It can't possibly operate on the names of the specific files that were found.
I have some better solutions...
First:
cd baselines
Then, like this to re-use your code:
find . -type f -name "README.md" -exec bash -c '
echo cp "$1" "./output/$(dirname "$1").md"
' -- {} \;
or
find . -type f -name "README.md" -exec bash -c '
for file; do
echo cp "$file" "./output/$(dirname "$file").md"
done
' -- {} +
Check https://mywiki.wooledge.org/UsingFind
or
for file in */README.md; do
echo cp "$file" "./output/$(dirname "$file").md"
done
Drop the echo
command when the output looks good for you (don't try to feed bash STDIN
).
cp ./combined_tree_local_conflict_obs/README.md ./output/./combined_tree_local_conflict_obs.md
cp ./global_density_obs/README.md ./output/./global_density_obs.md
Upvotes: 2
Reputation: 69208
What about this
$ find baselines -type f -name "README.md" -exec echo output/$(dirname {}|sed 's@/@_@/g').md \;
Upvotes: -1