Bash में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड
आपके पास बाइट्स हैं और आपको स्ट्रिंग चाहिए। एक टेक्स्ट फ़ाइल जो JSON बॉडी के अंदर रहनी चाहिए। एक इमेज जो कॉन्फ़िग लाइन में बसनी है। एक टोकन जो URL, एनवायरनमेंट वेरिएबल, या HTTP हेडर के रास्ते सफ़र करेगा। एक प्राइवेट की जो सर्टिफिकेट स्टोर में रखने वाली है। शेल में Base64 एन्कोडिंग का यही रोज़मर्रा का काम है, और शेल का जवाब है एक अकेला, छोटा, आश्चर्यजनक रूप से पोर्टेबल कमांड।
सौदा एक साँस में: Base64 कच्चे डेटा के हर तीन बाइट्स को 64 अक्षरों की वर्णमाला (A-Z, a-z, 0-9, साथ में + और /) के चार चरों में फिर से लिख देता है, और जब बाइट्स की संख्या तीन का गुणज न हो तो आख़िरी हिस्से में एक या दो = साइन की पैडिंग जोड़ देता है। इस साइट का होम पेज फ़ॉर्मेट को पूरा-पूरा समझाता है; यहाँ हम अपना वक़्त टेक्स्ट बनाने, गंतव्य के लिए सही बोली चुनने, और आँखें खोले हुए साइज़ का बिल चुकाने पर लगाते हैं। एक आँकड़ा जेब में रखने लायक़ है: एन्कोडेड रूप आमतौर पर मूल से लगभग एक-तिहाई बड़ा होता है, तीन बाइट्स पर चार चर, और यह बार-बार सामने आता रहता है।
कलाकारों की कतार छोटी है। coreutils का base64 कमांड (GNU या नया Rust uutils परिवार), उसी परिवार का URL-safe बोली के लिए basenc, coreutils-रहित मशीनों के लिए openssl base64, एम्बेडेड सिस्टम के लिए BusyBox अपलेट, और macOS पर BSD फ्लेवर। पाँच टूल, एक काम, और कुछ फ़्लैग्स जो जानने लायक़ हैं।
अपना एन्कोडर चुनें
इनमें से हर एक स्टैंडर्ड इनपुट या फ़ाइल से बाइट्स पढ़ता है और स्टैंडर्ड आउटपुट पर टेक्स्ट लिखता है, इसलिए सभी वही पाइपलाइन्स में बैठ जाते हैं। फ़र्क डिफ़ॉल्ट लाइन रैपिंग और उपलब्ध बोलीओं में है:
| टूल | कहाँ मिलता है | डिफ़ॉल्ट लाइन रैपिंग | कब हाथ लगाएँ |
|---|---|---|---|
base64 (coreutils) |
Linux, और macOS पर Homebrew से | 76 चर | डिफ़ॉल्ट विकल्प; एक लाइन के लिए -w 0 जोड़ें |
basenc (GNU coreutils) |
coreutils वाला Linux | 76 चर | आपको --base64url, base32, base16, या रिश्तेदार चाहिए |
openssl base64 |
हर जगह जहाँ OpenSSL इंस्टॉल है | 64 चर | coreutils गायब; एक लाइन के लिए -A |
busybox base64 |
Alpine, एम्बेडेड Linux | 76 चर | न्यूनतम सिस्टम; वही फ़्लैग्स, छोटे रूप में |
base64 (BSD/macOS) |
macOS, BSDs | कोई नहीं (एक लंबी लाइन) | नेटिव macOS का काम; विड्थ -b से सेट होती है |
उस रैपिंग वाली कॉलम को दो बार पढ़ें, क्योंकि यही परिवारों के बीच का ख़ामोश अंतर है। Coreutils और BusyBox डिफ़ॉल्ट में 76 पर रैप करते हैं, OpenSSL 64 पर, और BSD टूल बिल्कुल नहीं रैप करता। कोई भी गलत नहीं है; बस सबने अलग-अलग परंपराएँ उत्तराधिकार में ली हैं (MIME कहता है 76, PEM कहता है 64, और BSD टूल बस रैपिंग की आदत से पहले का है)। जब आपके कन्ज़्यूमर को फ़र्क पड़ता है, तो विड्थ खुद सेट करें और कभी डिफ़ॉल्ट पर भरोसा न करें।
पहले टेक्स्ट: अदृश्य नई लाइन
शेल में टेक्स्ट एन्कोड करना एक ही फँदाे से शुरू होता है: echo नई लाइन जोड़ देता है। "hello" के वही पाँच अक्षर, echo से गुज़रते ही, छह बाइट्स बन जाते हैं, और छठा बाइट आउटपुट में साथ चढ़ जाता है, अदृश्य और स्थायी:
echo "hello" | base64
यह aGVsbG8K प्रिंट करता है, और आख़िरी चर नई लाइन को एन्कोड करता है। इलाज वही है जिसे आप उन टेक्स्ट के लिए इस्तेमाल कर ही चाहिए जहाँ बाइट्स की गिनती मायने रखती है: फ़ॉर्मेट के साथ printf, बिना किसी सजावट के:
printf '%s' "hello" | base64
अब आउटपुट aGVsbG8= है, बिल्कुल पाँच बाइट्स का, और आख़िरी चर कोई ज़िंदा बाइट नहीं, पैडिंग साइन है। वही नियम हीर-स्ट्रिंग पर भी लागू होता है, जो echo की तरह आख़िर में नई लाइन जोड़ देती हैं: base64 <<< "hello" फिर से aGVsbG8K वाला वर्ज़न देता है। जब शक हो, तो एन्कोड करने से पहले खुद से पूछें कि आख़िरी बाइट क्या है।
जो भी चीज़ आप एक लाइन में चाहते हैं, उसे -w 0 जोड़ें (या नीचे दिए गए -w 0 के रिश्तेदार), जो आख़िरी नई लाइन को भी हटा देता है जो कमांड वरना निकालता:
printf '%s' "hello world and more" | base64 -w 0
यह एक साफ़, अखंड लाइन है बिना आख़िर की नई लाइन के, URL, JSON वैल्यू, या कॉन्फ़िग फ़ाइल में डालने के लिए तैयार, बिना किसी और समारोह के।
फ़ाइलें और रैप विड्थ
फ़ाइलें ही आम केस हैं, और हर इम्प्लीमेंटेशन FILE आर्गुमेंट लेता है, जो बाइट्स को शेल की क्वोटिंग की मशीन से बिल्कुल दूर रखता है:
base64 -w 0 report.pdf > report.b64
-w 0 के बिना, आउटपुट 76 चर पर रैप होकर आता है, जो कि MIME कन्ज़्यूमर को ठीक वही चाहिए:
base64 report.pdf > report.mime.b64
विड्थ वह डायल है जिसे आप कन्ज़्यूमर के हिसाब से संभालते हैं। सत्तारह RFC 2045 की MIME परंपरा है, छप्पन सर्टिफिकेट्स और कीज़ इस्तेमाल करने वाली PEM परंपरा है, और शून्य का मतलब है URL और APIs के लिए एक अखंड लाइन:
base64 -w 64 key.bin | head -2
अगर कन्ज़्यूमर Windows पर बसता है और CRLF लाइन एंडिंग्स की उम्मीद रखता है, तो रैप के बाद कन्वर्ट करें, पहले नहीं:
base64 -w 76 attachment.bin | sed 's/$/\r/' > attachment.crlf.b64
OpenSSL के रास्ते में, एक-लाइन मोड का बराबर है -A फ़्लैग, जो आख़िर की नई लाइन को भी दबा देता है:
openssl base64 -A < report.pdf
रैप की गई फ़ाइल भेजने से पहले, साइज़ की सैनिति चेक कुछ नहीं लागती और चौंकाने वाली गिनती में गलतियों को पकड़ लेती है (दो बार एन्कोड की गई फ़ाइल, गलत इनपुट से एन्कोड की गई फ़ाइल):
wc -c report.pdf
base64 report.pdf | wc -c
दूसरा आँकड़ा पहले से लगभग एक-तिहाई ज़्यादा होना चाहिए, साथ में हर रैप की गई लाइन की नई लाइन के लिए एक-एक बाइट। अगर यह बिल्कुल अलग-अलग निकले, तो रुकें और देखें कि आपने एन्कोडर को वाक़ई क्या दिया था।
URL-Safe Base64: दो शरारती चरों का बदलाव
स्टैंडर्ड वर्णमाला के दो चर, + और /, शरारती बच्चे हैं: URL क्वेरी स्ट्रिंग में + का मतलब स्पेस होता है, / पाथ सेपरेटर जैसा दिख सकता है, और दोनों स्ट्रिंग के URL, कुकी, या फ़ाइल नाम में दाखिल होते ही पर्सेंट-एन्कोडिंग ज़रूरी कर देते हैं। RFC 4648 का सेक्शन 5 इसे उसी बोली से हल करता है जो ठीक वही दो चर - और _ से बदल देती है, और पैडिंग हटा देती है, क्योंकि URL को बस सटीक बाइट लंबाई की घोषणा की ज़रूरत नहीं होती।
शेल की रेसिपी स्वैप और ट्रिम है, वर्णमाला के लिए tr से एक पार, और पैडिंग हटाने के लिए एक:
printf '\376\117\202' | base64 -w 0 | tr '+/' '-_' | tr -d '='
उन तीन बाइट्स की सामान्य एन्कोडिंग /k+C होती है, जो URL में बदसूरत दिखती; पाइपलाइन इसे _k-C में बदल देता है, चार चर जो कहीं भी सफ़र कर सकते हैं। यह स्वैप पोज़ीशनल है, इसलिए दिशा उलट-पुलट हो जानी आसान है: एन्कोडिंग tr '+/' '-_' की दिशा में जाती है (प्लस डैश बनता है, स्लैश अंडरस्कोर), और उल्टा, जो डिकोडिंग का काम है, tr '_-' '/+' की दिशा में। उलटी दिशा से कोई त्रुटि नहीं आती, यह बस अलग बाइट्स बना देता है, जो भेजने लायक़ बग की सबसे बुरी किस्म है।
जब भी स्ट्रिंग शेल के कंट्रोल से बाहर निकलती है, तब बोली मायने रखती है: JWT सेगमेंट्स, क्वेरी स्ट्रिंगs में टोकन्स, कुकीज़ या फ़ाइल नामों में वैल्यूज़, और वह हर आईडेंटिफ़ायर जिसे कोई और सिस्टम URL का हिस्सा मानकर पढ़ेगा। GNU का basenc इस बोली को नेटिव रूप से निकालता है, पैडिंग अभी भी अपनी जगह पर:
printf '%s' "hello" | basenc --base64url
अगर कन्ज़्यूमर को हटाए गए रूप चाहिए, जैसे कि ज़्यादातर को चाहिए, तो tr -d '=' से पैडिंग हटा दें।
शेल में JWT मिंटिंग
JSON Web Tokens API की दुनिया में URL-safe Base64 के सबसे ज़्यादा दिखने वाले कन्ज़्यूमर हैं। कॉम्पैक्ट JWT डॉट्स से जुड़े तीन base64url सेगमेंट्स होते हैं, RFC 7515 के अनुसार: हेडर, पेलोड, और सिग्नेचर। पहले दो सादा JSON होते हैं; सिग्नेचर पहले दोनों सेगमेंट्स को डॉट से जोड़ने पर बने बिनरी डायजेस्ट है, और यही वही चीज़ है जिसमें openssl का हाथ है।
key="supersecretkey"
h=$(printf '%s' '{"alg":"HS256","typ":"JWT"}' | base64 -w 0 | tr '+/' '-_' | tr -d '=')
p=$(printf '%s' '{"sub":"42","name":"homer"}' | base64 -w 0 | tr '+/' '-_' | tr -d '=')
s=$(printf '%s' "$h.$p" | openssl dgst -sha256 -hmac "$key" -binary | base64 -w 0 | tr '+/' '-_' | tr -d '=')
printf '%s.%s.%s\n' "$h" "$p" "$s"
यह एक कॉम्पैक्ट HS256 JWT प्रिंट करता है, जिसे किसी भी प्लेटफ़ॉर्म की कोई भी स्टैंडर्ड लाइब्रेरी मान लेगी। काम के बँटवारे को नोटिस करें: Base64 वाला हिस्सा वर्णमाला है, openssl dgst -sha256 -hmac वाला हिस्सा क्रिप्टोग्राफी है, और डॉट-जोड़ना फ़ॉर्मेट है। तीनों काम दिमाग़ में अलग रखें और पाइपलाइन साफ़ रहती है।
फील्ड के लिए तीन चेतावनियाँ। पहली: MAC पहले दोनों सेगमेंट्स के ASCII टेक्स्ट और डॉट पर गणना किया जाता है, इसलिए सिग्नेचर लगाने के समय सेगमेंट्स पहले से अपनी अंतिम base64url शक्ल में होने चाहिए; साइन के बाद दोबारा रैप या दोबारा पैडिंग करने से टोकन टूट जाता है। दूसरी: की टोकन के बाहर रहती है: सिग्नेचर साबित करता है कि किसने साइन किया, और की सीक्रेट को सीक्रेट बनाए रखती है। तीसरी: शेल स्क्रिप्ट में मिंटिंग एक टेस्टिंग और ऑटोमेशन टूल है, उस सर्वर की जगह नहीं जो इन टोकन्स को वाक़ई जारी करेगा और वेरिफाई करेगा, और alg: none के साथ मिंट किया गया टोकन कुछ भी साबित नहीं करता।
Data URIs: स्ट्रिंग्स के अंदर सवार फ़ाइलें
RFC 2397 data: URL स्कीम की परिभाषा देता है, और उसका Base64 रूप किसी फ़ाइल को URL के अंदर बसा देता है: पहले data:, फिर वैकल्पिक मीडिया टाइप, फिर ;base64 (जब पेलोड Base64-एन्कोडेड हो), फिर कॉमा, और फिर डेटा। मीडिया टाइप छोड़ दें तो डिफ़ॉल्ट text/plain;charset=US-ASCII है, जो जानने लायक़ एक फ़र्सा है, क्योंकि ज़्यादातर लोगों का मतलब इमेज या JSON दस्तावेज़ होता है, ASCII टेक्स्ट का नहीं।
printf 'data:text/plain;base64,%s\n' "$(printf '%s' "hi there" | base64 -w 0)"
यह data:text/plain;base64,aGkgdGhlcmU= प्रिंट करता है, एक पूरा, स्वयं-पर्याप्त URL जिसे ब्राउज़र खुशी-खुशी दिखाएगा। इमेज के लिए, असली मीडिया टाइप के साथ वही शक्ल:
printf 'data:image/png;base64,%s\n' "$(base64 -w 0 icon.png)" > icon.uri
नतीजे को HTML img टैग के src या CSS background में पेस्ट करें और इमेज दस्तावेज़ के साथ ही चली जाती है, बिने दूसरे HTTP रिक्वेस्ट के। फँदाे सब साइज़ के बारे में हैं: RFC ख़ुद कहता है कि स्कीम सिर्फ़ छोटे वैल्यूज़ के लिए उपयोगी है, ब्राउज़र अपनी-अपनी URL लंबाई सीमाएँ लगाते हैं, हर इनलाइन बाइट इमेज की अपनी साइज़ के ऊपर 33 पर्सेंट का अतिरिक्त खर्चा है, और data URIs से भरा पेज उन इमेजों के लिए बिना किसी कैशिंग कहानी वाला पेज है। छोटे आइकॉन्स और वन-ऑफ़ एम्बेडेड ग्राफिक्स के लिए यह आनंद है; फ़ोटो लाइब्रेरी के लिए यह बड़ी तकलीफ़ है।
सीक्रेट्स, कॉन्फ़िग और एनवायरनमेंट वेरिएबल्स
Base64 कॉन्फ़िगरेशन और सीक्रेट्स काम में एक ही खास वजह से सामने आता है: यह अराजक बाइट्स - स्पेस, क्वोट्स, और नई लाइन्स समेत - को एक स्ट्रिंग में बदल देता है जो export, कॉन्फ़िग लाइन, या JSON फ़ील्ड में बिना किसी क्वोटिंग स्टंट के बच जाती है। Kubernetes सबसे दिखने वाला उदाहरण है: सीक्रेट के .data के नीचे हर फ़ील्ड Base64 होता है, इसलिए शेल में सीक्रेट बनाना बस एन्कोडिंग है:
kubectl create secret generic app --from-literal=password='s3cret'
API सर्वर पासवर्ड को czNjcmV0 के रूप में .data के नीचे सहेजता है, और सीक्रेट तक पहुँच वाला कोई भी नोड इसे एक ही डिकोड में वापस पढ़ सकता है। वही चाल आपके अपने कॉन्फ़िग फ़ाइलों के लिए भी काम करती है:
export API_TOKEN_B64=$(printf '%s' "$API_TOKEN" | base64 -w 0)
या ऐसी फ़ाइल के लिए जो एप्लिकेशन स्टार्टअप पर पढ़ता है:
printf 'token=%s\n' "$(printf '%s' "$API_TOKEN" | base64 -w 0)" >> app.conf
यहीं वह चेतावनी आती है जो दीवार पर लहाने लायक़ है: Base64 एन्कोडिंग है, एन्क्रिप्शन नहीं। RFC 4648 का सिक्योरिटी सेक्शन इस बारे में सफ़ाई से कहता है कि एन्कोडिंग "आसानी से पहचाने जाने वाली जानकारी, जैसे पासवर्ड, को आँखों के लिए छुपा देती है, लेकिन कोई गणनात्मक गोपनीयता नहीं देती", और यही ठीक-ठीक ग़लतफ़हमी वाक़ई सिक्योरिटी घटनाओं का कारण बनी है, जब किसी ने "सुरक्षित" प्रोटोकॉल एक्सचेंज को बग रिपोर्ट में पेस्ट कर दिया और ग़लती से क्रेडेंशियल्स खुलकर पेश कर दी। अगर वैल्यू सीक्रेट होनी चाहिए, तो उसे एन्क्रिप्ट करें (और सहेजने के लिए साइफरटेक्स्ट को फिर Base64 करें); और अगर आपके पास सिर्फ़ Base64 है, तो एन्कोडेड वैल्यू को स्क्रीन से बाहर होते ही सादा टेक्स्ट मान लें।
Unicode, कैरेक्टर सेट्स और उनके नीचे के बाइट्स
एन्कोडर बाइट्स पढ़ता है, चर नहीं, और शेल उसे वही बाइट्स देता है जो लोकेल और कमांड ने बनाए हैं। UTF-8 टेक्स्ट के लिए यह आमतौर पर ठीक वही है जो आप चाहते हैं: héllo के é पहले से दो बाइट्स हैं, c3 a9, और एन्कोडिंग बस उन्हें साथ लेकर चलती है:
printf 'h\xc3\xa9llo' | base64
यह aMOpbGxv प्रिंट करता है, और दूसरी तरफ़ UTF-8 कन्ज़्यूमर héllo वापस पा लेता है, बाइट-दर-बाइट। तकलीफ़ तब शुरू होती है जब स्रोत UTF-8 न हो। उसी शब्द वाली Latin-1 फ़ाइल में é के लिए एक ही बाइट e9 होती है, और उन बाइट्स को सीधे एन्कोड करने से बने टेक्स्ट को वापस सिर्फ़ Latin-1 कन्ज़्यूमर पढ़ सकता है। पहले कन्वर्ट करें, दूसरे एन्कोड करें:
iconv -f ISO-8859-1 -t UTF-8 note.txt | base64 -w 0
दो और बाइट-लेवल की बातें। UTF-8 BOM, फ़ाइल के आगे के तीन बाइट्स, 77u/ में एन्कोड होता है, और अगर आप पहले इसे न हटाएँ तो वह आपके डिकोड हुए आउटपुट के आगे हमेशा बैठेगा:
sed '1s/^\xef\xbb\xbf//' file.txt | base64 -w 0
और लोकेल कभी एन्कोडिंग ख़ुद को नहीं बदलता, क्योंकि एन्कोडर एक बाइट मशीन है; वह बस बदलता है कि आपने क्या टाइप किया। जब आउटपुट गलत दिखे, तो एन्कोडिंग ख़ुद की न जाँचें, जिन बाइट्स को आपने दिया, उनकी जाँच करें।
ईमेल, APIs और अपलोड्स
ईमेल वही जगह है जहाँ Base64 ने अपनी आदतें सीखीं, और वही आदतें आज भी कन्वेंशन हैं। SMTP इतिहास में सिर्फ़ 7-बिट ASCII संभालता आया है, इसलिए अटैचमेंट्स Base64 के रूप में सफ़र करते हैं, 76 चर पर रैप, CRLF लाइन एंडिंग्स के साथ, RFC 2045 के अनुसार। MIME पार्ट के लिए उसी शक्ल को बनाना रैप और लाइन-एंडिंग कन्वर्ज़न है:
base64 -w 76 attachment.bin | sed 's/$/\r/' > attachment.mime
पुराना गार्ड एम्बेडेड सिस्टम में आज भी ड्यूटी पर है: BusyBox का uuencode, -m फ़्लैग के साथ, MIME Base64 बनाता है, जो पहचानने योग्य begin-base64 फ्रेमिंग में रैप होता है, और उसका भाई uudecode उसे वापस पढ़ लेता है:
busybox uuencode -m photo.jpg < photo.jpg > photo.uu
APIs और अपलोड्स वही सोच JSON के कपड़ों में इस्तेमाल करते हैं: बिनरी JSON फ़ील्ड के अंदर Base64 स्ट्रिंग बन जाता है, और curl उसे संभालकर ले जाता है। बॉडी को शेल वेरिएबल में बनाने से क्वोटिंग सच रहती है:
body="{\"file\":\"$(base64 -w 0 upload.bin)\"}"
curl -fsS -X POST https://httpbin.org/post -H "Content-Type: application/json" -d "$body"
यहाँ दो इंटरॉप फँदाे छिपे हैं। पहला: जाँच लें कि API कौन सी वर्णमाला चाहता है: कुछ स्टैंडर्ड Base64 की उम्मीद रखते हैं, कुछ URL-safe बोली की, और + चरों वाली स्ट्रिंग URL-safe एंडपॉइंट (या उलटा) भेजने से वैलिडेशन फेल होगी, या उससे बुरा, गलत बाइट्स में डिकोड हो जाएगी। दूसरा: डबल एन्कोडिंग का ध्यान रखें, वह क्लासिक बग जहाँ स्क्रिप्ट एक वैल्यू एन्कोड करती है और सर्वर उसे दोबारा एन्कोड कर देता है, और राउंड ट्रिप सुलझाने के लिए दो डिकोड चाहिए।
जब पेलोड बड़ा हो जाए
डिकोडर की तरह, एन्कोडर भी एक स्ट्रीमिंग मशीन है: यह चंक्स में पढ़ता है और चंक्स में लिखता है, इसलिए 10 GB के टारबॉल के लिए 13 GB RAM की ज़रूरत नहीं, और कमांड बड़े इनपुट पर मिनटों तक आराम से चलती है, मेमोरी का इस्तेमाल सपाट रखते हुए। साइज़ का हिसाब-किताब ही वही प्लानिंग टूल है जो आपको चाहिए: आउटपुट हर तीन इनपुट बाइट्स पर चार चर होता है, साथ में हर रैप की गई लाइन के लिए एक बाइट, इसलिए 300 MB की फ़ाइल लगभग 400 MB टेक्स्ट बन जाती है। किसी भी फ़ाइल के लिए त्वरित सच्चाई की जाँच:
base64 -w 0 big.bin | wc -c
जब टेक्स्ट ख़ुद को साइज़-सीमा वाले चैनल से पार करना हो (ईमेल अटैचमेंट की कैप, टिकट सिस्टम, IM संदेश), तो एन्कोडेड रूप को ही split करें, कभी कच्ची बिनरी को नहीं, ताकि हर चंक ऐसा सादा टेक्स्ट बचे जिसे आप पेस्ट, कंप्रेस, या आगे भेज सकते हैं:
base64 -w 0 big.bin | split -b 4000 - part_
यह 4000-चर वाले पार्ट्स की एक कतार बनाता है; रिसीवर उन्हें क्रम में cat करके वापस जोड़ता है और एक बार डिकोड करता है। और जब पेलोड कंप्रेस हो सके, तो एन्कोड करने से पहले कंप्रेस करें, क्योंकि Base64 डेटा की जो अनावश्यकता पहले से मौजूद है, उसके ऊपर और अनावश्यकता जोड़ता है: प्रोजेक्ट डायरेक्टरी का टारबॉल आमतौर पर उस 33 पर्सेंट Base64 अतिरिक्त कीमत लगने से पहले gzip से कई गुना छोटा हो जाता है:
tar czf - project/ | base64 -w 0 > project.b64
स्पीड आपकी सीमा नहीं बनेगी। ये एन्कोडर आधुनिक मशीन पर गिगाबाइट्स को एक सेकंड से कम में पार कर देते हैं: coreutils और OpenSSL इम्प्लीमेंटेशन से 200 MB की फ़ाइल लगभग एक-दसवें सेकंड में तैयार, और आम टूलों में सबसे धीमा BusyBox भी एक क्षण भर में ख़त्म (एक आधुनिक मशीन पर 200 MB के लिए लगभग एक-चौथाई सेकंड मापा गया, coreutils से कई गुना धीमा, लेकिन बॉटलनेक तक कहीं नहीं)। असली पाइपलाइन्स में बॉटलनेक लगभग हमेशा नेटवर्क होता है, एन्कोडिंग नहीं।
छोटे चर, जो डँसते हैं
एन्कोडिंग की तरफ़ के फँदाे डिकोडिंग वाले फँदाों से छोटे हैं, और यही बयान उचित है:
| फ़र्सा | क्या होता है | इलाज |
|---|---|---|
echo से एन्कोडर को खिलाया जाना |
आख़िर की नई लाइन आउटपुट में चढ़ जाती है, और आख़िरी चर उसे एन्कोड करता है | जहाँ बाइट्स की गिनती मायने रखती है, वहाँ printf '%s' |
| डिफ़ॉल्ट रैपिंग पर भरोसा करना | टूल के हिसाब से 76, 64, या शून्य; एक-लाइन कन्ज़्यूमर रैप इनपुट पर गला घोंट लेता है | खुद -w 0 (या कन्ज़्यूमर की माँगी विड्थ) सेट करें |
| आउटपुट में आख़िर की नई लाइन | लाइन-रैप मोड्स आख़िर में नई लाइन जोड़ते हैं, जो कैप्चर करने पर URL और JSON को दूषित करती है | एक लाइन के लिए -w 0, या कैप्चर $(...) से करें जो उसे हटा देता है |
URL में + या / |
क्वेरी स्ट्रिंग में प्लस स्पेस के तौर पर पढ़ा जाता है; दोनों पर्सेंट-एन्कोडिंग ज़रूरी कर देते हैं | URL में दाखिल होने वाली हर चीज़ के लिए URL-safe बोली |
tr की दिशा उलट-पुलट |
स्वैप वैलिड लेकिन गलत बाइट्स बनाता है, हरहाल कोई त्रुटि नहीं | एन्कोडिंग tr '+/' '-_' है; डिकोडिंग tr '_-' '/+' |
| पहले से एन्कोडेड वैल्यू को एन्कोड करना | डबल एन्कोडिंग, जिसे सुलझाने के लिए दो डिकोड चाहिए | एन्कोड करने से पहले जाँचें कि स्रोत पहले से Base64 तो नहीं है |
| इनपुट में UTF-8 BOM | हर डिकोड हुए आउटपुट के आगे तीन अतिरिक्त बाइट्स | BOM पहले हटाएँ: sed '1s/^\xef\xbb\xbf//' |
| असली सीक्रेट को Base64 के रूप में सहेजना | एक कमांड से वह वापस उलट जाता है; RFC में क्रेडेंशियल्स के लीक होने की वाक़ई घटनाएँ दर्ज हैं | गोपनीयता के लिए एन्क्रिप्शन; Base64 सिर्फ़ ट्रांसफ़र शक्ल के लिए |
| कन्ज़्यूमर की वर्णमाला मान लेना | स्टैंडर्ड बनाम URL-safe का असामंजस्य वैलिडेशन फेल कराता है या गलत डिकोड करता है | API की दस्तावेज़ पढ़ें; कन्ज़्यूमर की माँगी बोली में एन्कोड करें |
वह आदतें जो आपको सुरक्षित रखती हैं
- बाइट्स की गिनती का नाम लें। टेक्स्ट के लिए
printf '%s', फ़ाइलों के लिएFILEआर्गुमेंट, और जहाँ साइज़ मायने रखती है वहाँ भेजने से पहलेwc -cकी सैनिति चेक। - रैप खुद सेट करें। URL और JSON के लिए
-w 0, MIME के लिए-w 76, PEM के लिए-w 64। विड्थ कभी टूल के डिफ़ॉल्ट पर छोड़ें नहीं। - वर्णमाला गंतव्य के हिसाब से चुनें। ईमेल और फ़ाइलों के लिए स्टैंडर्ड, टोकन्स और URL के लिए URL-safe, और एन्कोड करने से पहले कन्ज़्यूमर की दस्तावेज़ जाँचें।
- एन्कोड करने से पहले कंप्रेस करें। जो भी पेलोड कंप्रेस हो सके, पहले
gzipयाtar czf; वह 33 पर्सेंट अतिरिक्त वह ही डेटा लगती है जो आप एन्कोडर को सौंपते हैं। - टेक्स्ट split करें, बिनरी नहीं। जब साइज़-सीमा रास्ते में खड़ी हो, तो एन्कोडेड रूप को
splitकरें ताकि हर चंक पेस्ट-सेफ़ रहे, और एक ही डिकोड से पहले क्रम में वापस जोड़ें। - JWT के तीनों काम अलग रखें। वर्णमाला, क्रिप्टोग्राफी, फ़ॉर्मेट: सेगमेंट्स एन्कोड करें, जोड़े गए सेगमेंट्स के ASCII टेक्स्ट पर सिग्नेचर लगाएँ, फिर निकालें। क्रम बदलें और टोकन टूट जाता है।
- Base64 को कभी एन्क्रिप्शन की जगह न खड़े करें। वैल्यू सीक्रेट है तो उसे एन्क्रिप्ट करें और फिर साइफरटेक्स्ट एन्कोड करें। सीक्रेट नहीं है तो कहे दें और चिंता छोड़ दें।
शेल में एन्कोडिंग का छोटा सा इतिहास
- 1980, बर्केली। Mary Ann Horton ने University of California, Berkeley पर Unix सिस्टमों के बीच ईमेल के रास्ते बिनरी फ़ाइलें पहुँचाने के लिए
uuencodeऔरuudecodeलिखे। इस नाम, "Unix से Unix तक एन्कोडिंग", की यह फ़ॉर्मेट की जन्म प्रमाण पत्र है, और अगले दस सालों तक शेल यूज़र्स इसी से एन्कोड करते हैं। - डायल-अप युग। UNIX पर uuencode और TRS-80 और Apple II पर BinHex, Macintosh एक क़दम पीछे, एक ही समस्या को अलग-अलग वर्णमालाओं से हल करते हैं, प्रत्येक सिर्फ़ उन चरों पर भरोसा करता है जिन्हें उसका अपना टर्मिनल प्रिंट कर सकता है।
- 1993। MIME ने RFC 1521 (बाद में RFC 2045) में ईमेल के लिए Base64 को मानक बना दिया, जिसमें 76-चर लाइन रैपिंग शामिल है जो coreutils का डिफ़ॉल्ट आज भी साथ रखता है।
- Linux पर 2006 से पहले।
base64कमांड है ही नहीं। शेल स्क्रिप्ट्सopenssl base64,uuencode -m, Perl, या Python की तरफ़ हाथ बढ़ाते हैं, और OpenSSL की आदत इतनी गहरी है कि बाहर की दुनिया की आधी पुरानी वन-लाइनर्स आज भी उसी से शुरू होती हैं। - 15 अगस्त 2006। coreutils 6.0 ने
base64कमांड निकाला, और उसकी NEWS फ़ाइल ने लिखा: "base64 एन्कोडिंग और डिकोडिंग (RFC 3548) का फीचर," और एक-कमांड का युग शुरू हुआ। कुछ महीने बाद, अक्टूबर 2006 में, RFC 4648 ने वर्णमाला परिवार को आधिकारिक रूप दिया, इस URL-safe बोली समेत जिसके लिए यह लेख बार-बार हाथ बढ़ाता है। - OS X 10.7। macOS ने अपना खुद का
base64निकाला, बिना डिफ़ॉल्ट रैपिंग वाला BSD फ्लेवर; इसीलिए "बस base64 चला दो" पोर्टेबल स्क्रिप्ट्स में प्लेटफ़ॉर्म चेक की माँग करता है। - 2024। coreutils 9.5 ने बदल दिया कि डिकोडर बिना-पैडिंग और नॉन-कैनोनिकल इनपुट का कैसा व्यवहार करते हैं, जो असलियत में इसका मतलब है कि एन्कोडर को फ्री पास मिलता है: आउटपुट जो पुरानी GNU वर्ज़न्स रिजेक्ट कर देती थीं, अब सफ़ाई से डिकोड होता है। फ़ॉर्मेट की एन्कोडर वाली तरफ़ वह स्थिर तरफ़ है; कदम बढ़ाने वाले डिकोडर ही थे।
- 2025। coreutils का Rust रीराइट (uutils) मौजूदा Ubuntu रिलीज़ का डिफ़ॉल्ट बन गया। वही कमांड, वही फ़्लैग्स, नया इंजन, और C वर्ज़न से उत्तराधिकार में मिला वही 76-चर डिफ़ॉल्ट।
छोटे-छोटे आश्चर्य
- फ़ॉर्मेट का नाम हर मशीन पर सच है।
printf 'base64' | base64GNU, uutils, BusyBox, OpenSSL, और macOS - किसी पर भी -YmFzZTY0देता है। यह 2006 से सच है और हमेशा सच रहेगा। - खाली फ़ाइल एन्कोड होने पर A की दीवार बनती है। इसे तीन NUL बाइट्स खिलाएँ और आउटपुट
AAAAहै, क्योंकि तीन शून्य-मान वाली बाइट्स वर्णमाला के चार शून्य-इंडेक्स पर मैप होती हैं, जिनमें से हर एक A से दिखता है।.b64फ़ाइल जो A की लंबी कतार से शुरू हो, उसमें आमतौर पर मूल फ़ाइल की ज़ीरो-पैडिंग (NUL बाइट्स) होती है, कोई रहस्य नहीं। - 33 पर्सेंट की कर में छूट नहीं। तीन बाइट्स पर चार चर, कोई कंप्रेशन नहीं, कोई दूसरा मौक़ा नहीं। एक ही रास्ता है कि डेटा पहले कंप्रेस किया जाए, इसीलिए
tar czfबड़े पेलोड पाइपलाइन्स का असली हीरो है। - दो चरों ने URL की सारी उलझन रचाई।
+और/ही वही वर्णमाला के सदस्य हैं जिनकी कभी बदले की ज़रूरत पड़ी, और फ़ॉर्मेट की पूरी एक बोली बस उन्हें रिटायर करने के लिए मौजूद है। छियासू और छियाइस, वर्णमाला के आख़िरी दो स्थान। - ग्यारह चर, छप्पन बिट्स। YouTube वीडियो ID एक 11-चर base64url स्ट्रिंग है, 64-बिट का नंबर जो URL के कपड़े पहने है; इसीलिए वह URL से गुज़रता है बिना एक भी पर्सेंट साइन के।
- Git के सबसे मशहूर इम्पोस्टर।
git diff --binaryके binary ब्लॉक्स Base64 जैसे दिखते हैं, लेकिनzसे शुरू होने वाली लाइन्स base85 शैली की अपनी बोली हैं। एक नज़र में पता चल जाता है कि यह आपकी वर्णमाला नहीं है; एक grep-करके-डिकोड-करने की चक्कर में बीस मिनट निकल जाते हैं। - हर टूल जानबूझकर अलग रैप करता है। MIME के लिए 76, PEM के लिए 64, BSD टूल पर शून्य: तीन डिफ़ॉल्ट, तीन उत्तराधिकृत परंपराएँ, एक फ़ॉर्मेट। विड्थ हमेशा आपकी चुनने वाली थी; टूल बस अलग-अलग डिफ़ॉल्ट याद रखते थे।
- एन्कोडर कभी आपके डेटा पर फेल नहीं होता। अपने डिकोडिंग रिश्तेदार के उलट, एन्कोडर का कोई ग़लत इनपुट नहीं, कोई ख़राबी नहीं, कोई सख़्त मोड नहीं। यह बाइट्स लेता है और अक्षर देता है, हर बार। इस लेख के बग सब वही हैं जो आप उसे सौंपने वाले बाइट्स में हैं, और जिस गंतव्य पर आप उन्हें भेजते हैं।
और जब सफ़र दूसरी दिशा में मुड़ता है, जब अक्षरों, अंकों, और कभी-कभी डैश या अंडरस्कोर वाली लंबी स्ट्रिंग आपके टर्मिनल में उतरे और आपको बाइट्स वापस चाहिए, तो नीचे लिंक किया गया संबंधित Base64 डिकोडिंग लेख उसी गहराई तक उस रस्म को कवर करता है, बिना-पैडिंग JWT सेगमेंट्स से लेकर डिकोडरों के छुपाने वाले हर नई-लाइन फँदा तक।
अंतिम अपडेट: 2026-09-08
संबंधित लेख: Bash में Base64 डिकोडिंग: एक सम्पूर्ण गाइड