{"id":20476077,"url":"https://github.com/t-h2o/tag-mp3","last_synced_at":"2026-04-24T03:31:41.013Z","repository":{"id":62464094,"uuid":"560601042","full_name":"t-h2o/tag-mp3","owner":"t-h2o","description":"An evening/night challenge. I learned to use `awk`","archived":false,"fork":false,"pushed_at":"2022-11-11T09:53:06.000Z","size":300,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-05T14:47:06.017Z","etag":null,"topics":["asciidoc","awk","bash","mp3info"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/t-h2o.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-01T21:14:40.000Z","updated_at":"2022-11-02T02:05:17.000Z","dependencies_parsed_at":"2023-01-21T14:46:58.038Z","dependency_job_id":null,"html_url":"https://github.com/t-h2o/tag-mp3","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/t-h2o/tag-mp3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t-h2o%2Ftag-mp3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t-h2o%2Ftag-mp3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t-h2o%2Ftag-mp3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t-h2o%2Ftag-mp3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/t-h2o","download_url":"https://codeload.github.com/t-h2o/tag-mp3/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t-h2o%2Ftag-mp3/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32208176,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T03:15:14.334Z","status":"ssl_error","status_checked_at":"2026-04-24T03:15:11.608Z","response_time":64,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asciidoc","awk","bash","mp3info"],"created_at":"2024-11-15T15:18:09.138Z","updated_at":"2026-04-24T03:31:40.984Z","avatar_url":"https://github.com/t-h2o.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"= MP3 tag with file name\n:sectnums:\n\nifdef::env-github[]\n++++\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://youtu.be/oqVy6eRXc7Q\"\u003e\u003cimg src=\"bob.png\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n++++\nendif::[]\n\nifndef::env-github[]\nimage::bob.png[align=center, link=\"https://youtu.be/oqVy6eRXc7Q\"]\nendif::[]\n\n\nI have recently downloaded the discography of Bob Marley through torrent and there is all the metadata within the torrent file, but not in the mp3 one. My goal is to collect the file name to tag the mp3 files.\n\n== My approach of creating my script\n\n=== Find a scripting language\n\nFor this kind of use (file manipulation), the bash script is a good choice.\n\n=== Collect data\n\n=== Scanf\n\nI already know about `scanf` function as I have used it in the C language. You give `scanf` a pattern` and it collects the data. I'm looking for something like `scanf` for bash script.\n\nI found on https://www.reddit.com/r/bash/comments/gxygww/why_isnt_there_scanf_or_equivalent_in_bash/[reddit] an example.\n\n[source, bash]\n----\n#!/bin/bash\n\npattern=\"Fan: (.+) rpm CPU die temperature: (.+) C\"\nif [[ \"Fan: fann rpm CPU die temperature: 42 C\" =~ $pattern ]]; then\n    fan=${BASH_REMATCH[1]}\n    cpu=${BASH_REMATCH[2]}\nfi\necho \"$fan\" \"$cpu\"\n----\n\n[quote]\nI use https://www.shellcheck.net/[shellcheck] to get good practices for creating shell scripts.\n\n=== A loop through each file\n\nI need a loop through each file. I found an example on https://w3guides.com/tutorial/bash-for-each-file-in-folder#bash-for-each-file-in-folder[w3guides.com]\n\n[source, bash]\n----\n#!/bin/bash\n\nfor filename in ./*; do\n    echo \"${filename}\"\ndone\n----\n\n=== Parse the filename\n\nFor each track on an album, we have to collect:\n\n* The title\n* The track number\n\nWe already have:\n\n* The artist\n* The album\n* The yar\n\nExample of filename for `Keep On Skanking`:\n\n[,plain]\n----\n01 - All In One.mp3\n02 - Keep On Moving.mp3\n03 - Keep On Skanking.mp3\n04 - Shocks Of Mighty (Soul Almighty Ve.mp3\n05 - Brand New Second Hand.mp3\n06 - Jungle Dub.mp3\n07 - Satisfy My Soul Babe (Version).mp3\n08 - Dracula (Version).mp3\n09 - Vs. Lee Scratch Perry Dreamland (Featuring Bunny Wailer).mp3\n10 - Comma Comma.mp3\n----\n\n=== awk\n\nAfter, a few researches I once again found this dark tool. I have had chosen to never learn to use this, because it's a very hard tool.\n\n[source,man]\n----\nNAME\n       awk — pattern scanning and processing language\n----\n\n==== Print the track number\n\nFor each `.mp3` files:\n\n[source, bash]\n----\n#!/bin/bash\n\nfor filename in ./\\.*mp3; do\n    echo \"${filename}\"\n    echo \"${filename}\" | \\\n        awk '{printf \"Track number: %d\\n\", substr($1, 3)}'\ndone\n\n# OUTPUT:\n# ./01 - All In One.mp3\n# Track number: 1\n----\n\nIf I take this filename as example `./01 - All In One.mp3`. The first element is `./01`. I have to do a substring, so begin my substring to the third character.\n\n[quote]\nI find it weird, this time the index start with 1 and not 0.\n\nWith the substring I get `01` instead of `./01`. And then I use `%d` of `printf` for convert to a number.\n\n==== Print the track title\n\n[source,bash]\n----\n#!/bin/bash\n\nfor filename in ./*\\.mp3; do\n\n    echo \"${filename}\"\n    echo \"${filename}\" | \\\n        awk '{printf \"Titel: \" }{i = 3} \\\n        {while (i + 1 \u003c= NF ) {printf \"%s \", $i; i++}} \\\n        {printf \"%s\\n\", substr($i, 0, length($i) - 4)}'\ndone\n\n# OUTPUT:\n# ./01 - All In One.mp3\n# Title: All In One\n----\n\n1. Just print `\"Title: \"`.\n2. Set `i = 3`.\n3. `While (i + 1 \u003c= NF)` print each element.\n\n[quote]\nNF: The number of fields in the current record.\n\n[start=4]\n4. Remove `./mp3`: Substing of the last element, start a the beginning of the string, stop at the end minus four.\n\n== Tag the track\n\nIt's the simplest step, after saving `track` and `title`. Just use `mp3info` for tagging them.\n\n[source,bash]\n----\n#!/bin/bash\n\nfor filename in ./*\\.mp3; do\n\n    track=$(echo \"${filename}\" | awk '{printf \"%d\", substr($1, 3)}')\n    title=$(echo \"${filename}\" | \\\n        awk '{i = 3} {while (i + 1 \u003c= NF ) {printf \"%s \", $i; i++}} \\\n        {printf \"%s\", substr($i, 0, length($i) - 4)}')\n    printf \"Track: %s\\tTitle: %s\\n\" \"${track}\" \"${title}\"\n    mp3info -t \"${title}\" -n \"${track}\" \"${filename}\"\ndone\n----\n\n== Bonus: Collect the folder data\n\nWe have to collect from the folder\n\n* The artist\n* The year of the album\n* The album name\n\nThe tracks are tidied up like this:\n\n[,plain]\n----\nBob Marley - 1967 - Keep On Skanking\n├── 01 - All In One.mp3\n├── 02 - Keep On Moving.mp3\n└── [...]\nBob Marley - 1970 - African Herbsman\n├── 01 - Riding High.mp3\n├── 02 - Lively Up Yourself.mp3\n└── [...]\n----\n\nFor remove the `./` or `./${foldername}`, we can use `basename`.\n\n== The final script\n\n[source,bash]\n----\n#!/bin/bash\n\nfor folder in ./Bob*; do\n\n    folder=$(basename \"${folder}\")\n    artist=\"Bob Marley\"\n    year=$(echo \"${folder}\" | awk '{printf \"%d\", $4}')\n    album=$(echo \"${folder}\" | awk '{i = 6} {while (i \u003c= NF ) {printf \"%s \", $i; i++}}')\n\n    for filepath in \"${folder}\"/*\\.mp3; do\n\n        filename=$(basename \"${filepath}\")\n\n        track=$(echo \"${filename}\" | awk '{printf \"%d\", $1}')\n        title=$(echo \"${filename}\" | \\\n        awk '{i = 3} {while (i + 1 \u003c= NF ) \\\n        {printf \"%s \", $i; i++}} \\\n        {printf \"%s\", substr($i, 0, length($i) - 4)}')\n\n        mp3info -a \"${artist}\" \\\n            -l \"${album}\" \\\n            -y \"${year}\" \\\n            -t \"${title}\" \\\n            -n \"${track}\" \"${filepath}\"\n\n        echo mp3info -a \"${artist}\" \\\n            -l \"${album}\" \\\n            -y \"${year}\" \\\n            -t \"${title}\" \\\n            -n \"${track}\" \"${filepath}\"\n    done\ndone\n----\n\n== Conclusion\n\nAs I said `awk` is a dark and a complicated tool. But after spending a few hours reading manuals and manipulating `awk`, I got comfortable with it. With this project I learned basics of `awk` and new knowledge on `AsciiDoc` and `bash`.\n\n=== The real conclusion\n\nAll the Bob Marley's tracks are tagged and tidied !\n\nimage::cmus.png[\"cmus view\"]\n\n== Other\n\n=== Pattern matching\n\n. Use the bash shell\n. Enable the `extglob`\n\n[source,bash]\n----\nshopt -s extglob\n----\n\n[start=3]\n. Try to list all but not MP3 files with\n\n[source,bash]\n----\nls !(*.mp3)\n----\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft-h2o%2Ftag-mp3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ft-h2o%2Ftag-mp3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft-h2o%2Ftag-mp3/lists"}