{"id":"code-dev-sh-posix-loop-filenames-spaces","audience":"developers","category":"coding","track":"coding","technique":"constraints","title":"Fix a shell loop that breaks on filenames with spaces","free":false,"task":"The thumbnail job breaks on photos whose names contain spaces. It runs under /bin/sh on Alpine (BusyBox ash), so the fix must be POSIX sh: no [[ ]], no arrays, no bash-only syntax. Loop over a quoted glob instead of ls, skip the loop body when nothing matches with [ -e \"$f\" ] || continue, and keep the same convert options and the thumb- output names. New rules from ops: skip any photo that already has a thumbnail, using [ -e \"$DEST/thumb-$name\" ] && continue, and after each convert print made: $name. Return only the loop in exactly 1 code block of at most 10 lines, with under 25 words of text.","materialLabel":"The script and the job log (attached to your prompt automatically)","material":"#!/bin/sh\n# make-thumbs.sh - runs every 10 min in the \"thumbs\" container (alpine:3.20)\nset -eu\n\nSRC=\"/data/uploads\"\nDEST=\"/data/thumbs\"\n\nmkdir -p \"$DEST\"\n\nfor f in $(ls $SRC/*.jpg); do\n  name=$(basename $f)\n  convert $f -resize 320x320 -quality 82 $DEST/thumb-$name\ndone\n\necho \"thumbs done: $(date -u +%H:%M)\"\n\n---- job log, 09:40 UTC ----\nconvert: unable to open image '/data/uploads/Beach': No such file or directory\nconvert: unable to open image 'Day.jpg': No such file or directory\nthumbs done: 09:40\n\n---- job log, 09:50 UTC (uploads folder was empty) ----\nls: /data/uploads/*.jpg: No such file or directory\nthumbs done: 09:50\n\n---- notes ----\nFiles that failed: \"Beach Day.jpg\", \"Team photo 2.jpg\"\nContainer has no bash installed (we checked: /bin/bash missing).\nImageMagick 7 is installed as convert.","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":10},{"type":"maxProseWords","max":25}],"mistakes":[{"title":"Getting bash","why":"The AI may fix it with arrays, [[ ]] or a bash shebang, but this container has no bash.","fix":"Add: \"POSIX sh only (BusyBox ash): no [[ ]], no arrays, no bash.\"","checks":["r0"]},{"title":"No log line","why":"Ops greps the job log for each thumbnail, and the AI won't print one unless asked.","fix":"Add: \"After each convert, echo \\\"made: $name\\\".\"","checks":["r1"]},{"title":"Using find or ls again","why":"find | while read still breaks on some names, and ls output was the bug in the first place.","fix":"Say: \"Loop over a quoted glob: for f in \\\"$SRC\\\"/*.jpg; quote every variable.\"","checks":["r0"]},{"title":"Forgetting the empty folder","why":"With no matches, the glob stays literal and convert runs on '*.jpg'.","fix":"Add: \"Start the loop body with [ -e \\\"$f\\\" ] || continue.\"","checks":["r0"]},{"title":"Missing the ops rule","why":"The AI can't know existing thumbnails should be skipped.","fix":"Add: \"Skip a photo if \\\"$DEST/thumb-$name\\\" already exists.\"","checks":["r2"]},{"title":"A rewritten script","why":"The AI reprints the whole script, changes options or adds logging.","fix":"Add: \"Only the loop, same convert options, at most 10 lines, no text.\"","checks":["codeBlocks","maxCodeLines","maxProseWords"]}],"promptHints":[{"label":"Names the shell","patterns":["posix","/bin/sh","busybox","ash","no bash"]},{"label":"Asks for a quoted glob","patterns":["glob","quote","not ls"]},{"label":"Asks for the log line","patterns":["made:","echo","log"]},{"label":"Skips existing thumbnails","patterns":["already","exist","skip"]},{"label":"Asks for only the loop","patterns":["only the loop","just the loop"]}],"hint":"'Fix this' gets you a bash script, and this box has no bash. Say which shell it runs in, how to loop, the new skip rule, and what must stay the same.","rubricLabels":["Loops with a quoted glob like for f in \"$SRC\"/*.jpg (not ls or find), quotes every variable, uses only POSIX sh (no [[ ]] or arrays) and includes [ -e \"$f\" ] || continue","After each convert, prints exactly made: $name (e.g. echo \"made: $name\")","Skips photos that already have a thumbnail with [ -e \"$DEST/thumb-$name\" ] && continue (or equivalent) before convert, keeps -resize 320x320 -quality 82, and only the loop is returned"]}