Debugging some confusing behaviour with find and xargs
Use the --verbose flag to see what xargs is doing; don’t rely on find to return files in a consistent order.
I was trying to write a GitHub Actions workflow to format my Caddyfiles. I’d written a command to find all the Caddyfiles in my repo, and pass them to caddy fmt:
find . -name Caddyfile | xargs caddy fmt --overwrite
Thsi command seemed to work when I ran it locally on my Mac, but it didn’t do what I expected when it ran on Ubuntu in GitHub Actions. It took me a while to work out what was going on!
I learnt two useful debugging techniques along the way:
- Use the - --verboseflag to see what- xargsis doing. This will print the exact command which is run by- xargs.- $ ls *.jpg | xargs echo 26704189108_9e71d865d8_b.jpg 51119071159_905212c30f_b.jpg 8076936667_c5605863a9_o.jpg $ ls *.jpg | xargs --verbose echo echo 26704189108_9e71d865d8_b.jpg 51119071159_905212c30f_b.jpg 8076936667_c5605863a9_o.jpg 26704189108_9e71d865d8_b.jpg 51119071159_905212c30f_b.jpg 8076936667_c5605863a9_o.jpg $ ls *.jpg | xargs --verbose -I '{}' echo '{}' echo 26704189108_9e71d865d8_b.jpg 26704189108_9e71d865d8_b.jpg echo 51119071159_905212c30f_b.jpg 51119071159_905212c30f_b.jpg echo 8076936667_c5605863a9_o.jpg 8076936667_c5605863a9_o.jpg- In particular, I could see whether the command I was running with - xargswas being called with all the arguments at once, or passed them individually.
- Files discovered by - findmay be in a different order on different OSes.- This was the heart of my issue – I was passing all my filenames in one big list, and - caddy fmtwill only format the first path it gets. Because the order of files is different on different OSes, I was getting different behaviour.- Compare: - $ # macOS $ find . -name Caddyfile -o -name '*.Caddyfile' ./Caddyfile ./caddy/gone.Caddyfile ./caddy/redirects.Caddyfile $ # Ubuntu $ find . -name Caddyfile -o -name '*.Caddyfile' ./caddy/gone.Caddyfile ./caddy/redirects.Caddyfile ./Caddyfile- In hindsight, this makes sense – there are multiple ways you might search a directory tree, and one isn’t necessarily better than the other. - Don’t rely on the order to be consistent! - I’ve had a brief glance at the man page, and - finddoes have some options for controlling the ordering – for example, you can use- -d/- --depthto do a depth-first search (process each directory’s contents before the directory itself).- This reminds me of when I discovered - WalkDir::contents_first()and- os.walk(topdown: bool)while writing- emptydir.