GNU Coreutils की डिकोडिंग (2018)
(maizure.org)- GNU coreutils 8.3 source structure को utility-दर-utility पढ़ते हुए समझाने वाला एक दीर्घकालिक व्याख्या प्रोजेक्ट, जो command-line tool design सीख रहे शुरुआती programmers के लिए बनाया गया है
- यह उपयोग-निर्देश documentation नहीं, बल्कि source code पढ़ने में मदद करने वाला material है; इसमें हर command के namespace, execution flow, design decisions, algorithms, UNIX और शुरुआती Coreutils lineage को साथ में कवर किया गया है
- coreutils में छोटे और single-purpose C programs बहुत हैं, इसलिए global variables, macros,
goto, लंबी functions जैसे आधुनिक नज़रिए से rough code patterns को भी ऐतिहासिक context में पढ़ना चाहिए - common flow initialization, option parsing, input handling, system calls चलाने और failure handling से होकर जाता है;
getopt_long(),fts, gnulib helpers बार-बार दिखाई देते हैं - contribution या बदलाव से पहले यह जांचना चाहिए कि क्या existing tools से उसे reproduce किया जा सकता है, क्या वह backward compatibility तोड़ता है, और POSIX से कितना अलग है
GNU coreutils व्याख्या प्रोजेक्ट का दायरा
- यह GNU coreutils 8.3 की सभी utilities को समझाने वाला दीर्घकालिक project है
- target readers command-line utilities के design को देख रहे शुरुआती programmers हैं
- जिस utility में आपकी रुचि हो, उसका source code पढ़ते समय इसे background explanation के तौर पर साथ देखने के लिए यह सबसे उपयुक्त है
- यह material user manual नहीं है; असल usage के लिए man pages देखें
- project कई phases से बना है
- Phase 1: हर utility के namespace और execution overview pages पूरे
- Phase 2: प्रमुख design decisions और algorithms, UNIX और शुरुआती Coreutils lineage, ज्यादा उपयोगी source walkthroughs, source code evolution visualization पूरे
- Phase indefinite: हर utility के line-by-line code walkthroughs लंबे समय तक जारी रहने वाले काम के रूप में बचे हैं
- line-by-line notes इकट्ठा करने के लिए GitHub repo उपलब्ध है
utility-wise explanation pages
- हर command name उस utility को decode करने वाले detailed page से जुड़ा है
- हर page में discussion, source code और walkthrough शामिल हैं
- bold में दिखी utilities Phase 2 में expanded items हैं
- list में
arch,base64,cat,chmod,cp,date,dd,df,ls,rm,sort,tail,tr,wc,yesआदि GNU coreutils commands शामिल हैं
code पढ़ने से पहले जानने योग्य assumptions
- coreutils की कई utilities का इतिहास लगभग 30 साल पुराना है, और कई लोगों ने लंबे समय में इस code को modify किया है
- ज्यादातर छोटी और single-purpose के लिए बनी छोटी programs हैं
- आम तौर पर single source file programs हैं
- यह ऐसा code नहीं है जिसे बहुत लंबे समय तक टिकने या अपने role से ज्यादा expand होने के लिए design किया गया हो
- global variables, macros,
goto, nestedswitch/loops वाली लंबी functions जैसे patterns दिखते हैं
- POSIX की जानकारी code समझने में अहम है
- Utility Syntax Guidelines से शुरू करना बेहतर है
- POSIX input और output की interoperability define करता है, लेकिन असल काम करने का तरीका implementation पर छोड़ता है
- GNU coreutils POSIX का सख्ती से पालन न करे, फिर भी permission bits, uid/gid, environment variables, exit status जैसे concepts गहराई से बसे हैं
- portability issues की वजह से coreutils gnulib पर काफी निर्भर है
- लगभग हर utility में ऐसे gnulib functions शामिल हैं जो कई systems पर common issues handle करने के लिए design किए गए हैं
- coreutils को
bash,zsh,kshजैसे shells के support के साथ चलना माना जाता है- shell fork/clone से utility चलाता है, arguments pass करता है, environment set करता है, pipes के जरिए input/output redirect करता है, और exit value preserve करता है
- GNU coreutils मूल रूप से shell, text और file utilities नाम के तीन packages से शुरू हुआ था, और same type की utilities कई design patterns share करती हैं
basic design patterns
- ज्यादातर CLI utilities में आम तौर पर तीन stages होते हैं
- flags, options, localization आदि तैयार करने वाला setup stage
- input पढ़कर execution parameters तय करने वाला argument parsing stage
- input तैयार करके उसे एक या अधिक system calls को देने वाला processing/execution stage
- execution के दौरान कई points पर constraints check किए जा सकते हैं और failure हो सकता है
- अलग-अलग exit statuses problem की जगह का hint दे सकते हैं
EXIT_FAILUREअक्सर generic failure status के रूप में इस्तेमाल होता है
- failure के बाद user को feedback दिया जाता है
- utility variants तीन groups में बंटते हैं
- Trivial utilities: कुछ lines के macros define करने के बाद किसी दूसरी utility का source include करती हैं, और macros से specific flow control force करती हैं। उदाहरण:
arch,dir,vdir - Wrapper utilities: setup और option parsing के बाद command-line options को सीधे system call arguments में pass करती हैं। system call का result ही utility का result बनता है, और अपनी processing कम होती है। उदाहरण:
link,whoami,hostid,logname - Full utilities: setup, option/argument parsing, input data processing और कई system calls चलाने तक सब कुछ रखती हैं; ज्यादातर utilities इसी category में आती हैं
- Trivial utilities: कुछ lines के macros define करने के बाद किसी दूसरी utility का source include करती हैं, और macros से specific flow control force करती हैं। उदाहरण:
common initialization और option parsing
- सभी utilities में
main()की शुरुआत में छोटा initialization procedure होता हैinitialize_main (&argc, &argv);set_program_name (argv[0]);setlocale (LC_ALL, "");bindtextdomain (PACKAGE, LOCALEDIR);textdomain (PACKAGE);atexit (close_stdout);
- यह preprocessing code internationalization और exit-time behavior registration जैसे administrative tasks handle करता है, और individual utility के core behavior को प्रभावित नहीं करता
- command-line option parsing में Getopt tools की मुख्य भूमिका है
- short options
-, long options--prefix का उपयोग करते हैं - short options string से define होते हैं, और long options struct का उपयोग करते हैं
- short option string में अगर argument नहीं है तो सिर्फ character लिखा जाता है, required argument के लिए
:, optional argument के लिए::लगाया जाता है killकी short option stringLln:s:tका मतलब है किL,l,tमें arguments नहीं हैं औरn,sमें arguments चाहिए- सभी utilities में
getopt_long()next option return करने के लिए इस्तेमाल होता है optind,argv[]array में next argument की position दिखाता हैoptargoption argument value की ओर इशारा करने वाला character pointer है
- short options
file system traversal और system call helpers
- Unix-like systems में file system traversal को आसानी से handle करने के लिए अक्सर fts library का support होता है
fts_open()याxfts_open()से path सेFTSstructure बनाया जाता है- tree की file या directory nodes
FTSENTstructure से represent होती हैं fts_read()call करने परFTSENTgenerate होता है, और यही process tree traversal हैFTSENT->fts_infofield अक्सर item description में इस्तेमाल होता है, और item को कैसे process करना है यह तय करने में उपयोग किया जाता है
- coreutils, libc द्वारा दिए गए functions के अलावा भी कई system call wrappers और helpers इस्तेमाल करता है
- write handling में
write()system call wrapper औरfwrite()जैसे libc functions के अलावाfull_write()जैसे non-standard functions दिखते हैं- full_write() hard failure न हो तो writing retry करता रहता है
- safe_write() interrupt होने पर भी
write()system call retry करता है ddकाiwrite(),splitकाcwrite()जैसे write helpers भी हैं जो केवल single utility में इस्तेमाल होते हैं
common functions और code idioms
- सभी utilities कम से कम
main(),usage(),_()तीन functions इस्तेमाल करती हैं usage()input parameters, meaning और सही syntax सहित help दिखाता है_()system.hमें defined macro है, और plain strings को GNU gettext.h की Native Language Support functionality से जोड़ता है- user को दिखाई जाने वाली string हो तो आम तौर पर इस macro से wrap की जाती है
- ज्यादातर non-trivial utilities में common code lines repeat होती हैं
#include "system.h"system-dependent macros, variables और non-standard utility functions define करता हैPROGRAM_NAMEutility का official name define करता है और version checking में इस्तेमाल होता हैAUTHORSutility authors define करता है और version checking में इस्तेमाल होता हैemit_try_help()failed output के बाद help suggestion print करता हैemit_ancillary_info (PROGRAM_NAME)command-specific output के बाद common extra help info print करता हैinitialize_main(&argc, &argv)VMS की built-in wildcard expansion handle करता है, और अधिकांश दूसरे operating systems में remove हो जाता हैatexit(close_stdout)program exit पर buffered streams को flush और close करने वाला function register करता है
- C idioms भी आते हैं जो beginners के लिए अपरिचित हो सकते हैं
!!double unary NOT operation है, जिसका उपयोग value को boolean में force-convert करने के लिए होता हैdo { ... } while (0)multiple-statement macro को wrap करके preprocessor substitution के बाद tokenization सुरक्षित बनाने वाला non-loop pattern है
maintenance approach और contribution से पहले checks
- coreutils जैसे active projects आम तौर पर तीन maintenance flows से evolve होते हैं
- project-wide changes सभी utilities की structure और dependencies में फैले बड़े changes होते हैं
- 1995 में GNU gettext project के जरिए Native Language Support जोड़ा गया, जिससे text output की अधिकांश lines में
_()macro आया - 1996 में internationalization support बढ़ा और
main()में कई initialization code जोड़े गए - 1995 में usage output में utility purpose का छोटा description जोड़ा गया
- 2003 में VMS wildcard support जोड़ा गया, जो
initialize_main()function में दिखता है - 2016 में failure paths में compiler warnings से बचने के लिए
die()macro ने अधिकांशexit()औरerror()functions की जगह ली
- 1995 में GNU gettext project के जरिए Native Language Support जोड़ा गया, जिससे text output की अधिकांश lines में
- utility-specific changes bug fixes, new features और optimizations में बंटते हैं
join,sort,uniq2016 के patch से पहले overflow attacks के लिए vulnerable थेdfमें 2013 में--outputoption जोड़ा गयाyesकी performance बेहतर buffering से improve हुई
- annual maintenance में कम से कम सभी utilities के copyright years update करना शामिल होता है
- FSF address update जैसे administrative changes भी होते हैं और execution पर असर नहीं डालते
- contributors को पहले GNU project page, contribution guidelines, rejected features, mailing list archives देख लेना बेहतर है
- code लिखने से पहले जांचने के लिए तीन सवाल हैं
- क्या existing tools से वही function reproduce किया जा सकता है
- क्या contribution backward compatibility तोड़ता है
- क्या proposed behavior POSIX से काफी अलग है
- अगर भरोसा न हो तो mailing list पर community से पूछने की प्रक्रिया recommend की जाती है
दिलचस्प details
- सबसे छोटी utility
falseहै और 2 lines की है;arch,dir,vdirभी बराबर हैं - सबसे छोटी standalone utility
trueहै, 80 lines की; इसका first version लगभग minimal C program जैसा है - सबसे लंबी utility
lsहै, 5308 lines की - कई utilities 1970s के Research UNIX तक जाती हैं, और कुछ Multics तक
- सबसे पुराना conceptual ancestor लगभग 1963 का CTSS
LISTFcommand है, जो बाद में छोटा होकरlsबनाLISTF, 1962 design paper की original 18 utilities में से एक है
ddका अनोखा syntax 1960s की शुरुआत के OS/360 job control language की याद दिलाता हैsortmultithreading का उपयोग करने वाली इकलौती utility हैfmtfeature cost का उपयोग करके line और paragraph optimization दिखाता हैyeshigh-performance output के लिए page-aligned memory buffer का उपयोग करता हैdfdevice metadata का उपयोग करता है, औरduहर file check करता है, इसलिएdfतेज हैcksumमें normal execution और CRC-32 table generation के लिए दो entry points हैंechoमें कोई failure condition नहीं हैtestऔरexprका design common utility structure से काफी अलग हैsuको originally coreutils/shellutils में maintain किया जाता था
देखने लायक implementations
- shuf और shred random numbers के लिए ISAAC cipher) का उपयोग करते हैं
- shuf Reservoir sampling का उपयोग करता है
- sum legacy System V और BSD checksums का उपयोग करता है
- expr left-associative expression evaluation का उपयोग करता है
- shred Secure overwrite का उपयोग करता है
- cksum CRC-32 checksum calculate करता है
- sort Merge sort का modified रूप इस्तेमाल करता है
- factor mathematical implementation elements देखने लायक utility है
sponsorship notice
- personal sponsorship लेने की setup नहीं है
- अगर आप समय या पैसे share करना चाहते हैं, तो Free Software Foundation में contribute कर सकते हैं
1 टिप्पणियां
Hacker News की राय
अगर यह कहा जा रहा है कि “इन utilities में से कई लगभग 30 साल पुरानी हैं, और इस दौरान कई लोगों ने उन्हें modify किया है”, लेकिन साथ ही “इन्हें लंबी उम्र या अपनी भूमिका से आगे expand होने के लिए design नहीं किया गया था”, तो मैं देखना चाहूंगा कि लेखक के हिसाब से लंबी उम्र को ध्यान में रखकर बना program कैसा होता है
जिज्ञासा है कि क्या कोई ऐसा program है जो 30 साल तक टिक चुका हो, या ऐसा जिसे वे मानते हों कि अगले 30 साल चलेगा
मैंने अपनी छोटी-सी programming language को test करने के लिए bash और coreutils से test framework बनाया था. शुरुआत में “proper” language न इस्तेमाल करने पर थोड़ा अजीब लगा, लेकिन असल में यह बहुत अच्छी तरह चला और parallel execution भी हुआ
सिर्फ एक चीज test नहीं कर पाया: program का
argv[0]. कितने भी combinations करके देखे, मनचाहा behavior ठीक से नहीं बन पाया, इसलिए मैंने coreutils कोenvfeature request और patch भेजा: https://lists.gnu.org/archive/html/coreutils/2023-08/msg0006...लगता है यह शामिल हो जाएगा, यानी एक पुराने program में नया feature जुड़ने जैसा होगा
लेखक ने जिन practices की सूची दी है, उन्हें बड़े programs में आम तौर पर scale बढ़ने के साथ maintainability को नुकसान पहुंचाने वाली चीजों के रूप में criticize किया जाता है
संदर्भ के लिए उपयोगी सामग्री:
GNU coreutils testing का तरीका: https://www.pixelbeat.org/docs/coreutils-testing.html
coreutils के हर command की पड़ताल: https://ratfactor.com/slackware/pkgblog/coreutils
GNU Coreutils के साथ command-line text processing: https://learnbyexample.github.io/cli_text_processing_coreuti... — मेरी ebook, जिसमें 20 से ज्यादा text processing tools cover हैं
संबंधित लेख:
Decoded: GNU Coreutils (2018) - https://news.ycombinator.com/item?id=29871037 - जनवरी 2022, 7 comments
Decoded: GNU coreutils (2019) - https://news.ycombinator.com/item?id=26411908 - मार्च 2021, 38 comments
Decoded: GNU Coreutils - https://news.ycombinator.com/item?id=20328650 - जुलाई 2019, 55 comments
एक दिलचस्प बात: macOS पर Homebrew से coreutils install करने पर, क्योंकि macOS में पहले से
od(1)मौजूद है, coreutils काodgod(1)के रूप में install होता हैअगर लेखक यह देख रहे हों, तो कम से कम एक गलती है. shred page का short description[0] असल में
csplitका description[1] है, और यह कुछ ऐसा होना चाहिए: “file contents को छिपाने के लिए overwrite करता है और optionally delete करता है”[0]: https://maizure.org/projects/decoded-gnu-coreutils/shred.htm...
[1]: https://maizure.org/projects/decoded-gnu-coreutils/csplit.ht...
मुझे पता नहीं था कि ऐसा कुछ है, बढ़िया है.
yesजैसा simple tool भी यह देखने के लिए काफी रोचक है कि standard output पर लिखने वाली utility का basic code कैसे लिखा जाता हैhttps://maizure.org/projects/decoded-gnu-coreutils/yes.html
उदाहरण के लिए, output को बहुत तेजी से लिखने का तरीका सीखने के लिए एक minimal example होना अच्छा है. program इतना छोटा है कि lines की संख्या ज्यादा मायने नहीं रखती, और अगर author तरीका जानता है तो उसे जितना हो सके उतना fast बनाना बेहतर है, ताकि वह bottleneck न बने
बेसिक utilities की सूची के तौर पर काफ़ी दिलचस्प। UNIX बहुत लंबे समय से इस्तेमाल किया है, फिर भी
shred,shuf,factorजैसी चीज़ों के बारे में कभी सुना नहीं थाएक बार
चलाकर देखने का मन होता है कि यह खुद को मारने से पहले कितनी दूर तक जाता है। बेशक virtual machine या ऐसे डिवाइस पर जिसे आसानी से दोबारा flash किया जा सके
ddसे करके देखा था, लेकिन वह अंत तक पूरा हो गया और काफ़ी एंटी-क्लाइमैक्स लगाउम्मीद थी कि crash होगा या कम से कम connection टूट जाएगा, लेकिन kernel और
sshd,bashअब भी memory में थे, इसलिए एक ऐसे prompt पर वापस आ गया जिससे कुछ भी ठीक से नहीं किया जा सकता थाETXTBUSY), इसलिएshredखुद को याfindको खराब नहीं कर पाएगामुझे यह बात पसंद है कि
/bin/trueअसल में fail होकर false return कर सकता है। तकनीकी तौर पर “Not/bin/false” call ज़्यादा robust हो जाता है: https://github.com/coreutils/coreutils/blob/master/src/true....पता है कि ऐसा लगभग कभी नहीं होगा, लेकिन बस मज़ेदार है
truecommand में एक और दिलचस्प बात यह है कि यह ज़रूरत से कहीं ज़्यादा complex हो गया हैपहले एक exercise:
trueसचमुच इसी तरह शुरू हुआ था। काफ़ी Zen जैसापहला बदलाव legal वजहों से था। हर code में copyright notice होना चाहिए था, और खाली file में भी चाहिए था। इसलिए कुछ न होने वाली file, सिर्फ copyright notice वाली file बन गई, और सवाल उठा कि “क्या कुछ भी नहीं पर copyright लगाया जा सकता है?” AT&T ने कोशिश की थी
फिर किसी ने कहा कि program well-defined होना चाहिए और Unix के accidental behavior पर निर्भर नहीं रहना चाहिए, और उस समय यह काफ़ी reasonable था। इसलिए
trueमें आखिरकार code आया, और वह codeexit 0थाफिर किसी ने कहा कि system utilities को shell के बजाय C में लिखना चाहिए ताकि वे तेज़ हों, और OpenBSD में उसका एक अच्छा उदाहरण अब भी है: http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr....
किसी समय GNU bureaucracy बीच में आई और कहा कि हर program को
-hflag support करना चाहिए, फिर कहा कि हर program को locale support करना चाहिए, इसलिए आज का GNUtrueहैरानी की बात है कि 80 lines का हो गया: https://github.com/coreutils/coreutils/blob/master/src/true....इसे ठीक भी माना जा सकता है, लेकिन जिस program की परिभाषा ही “कुछ न करना और सफल होना” है, उसके लिए code काफ़ी ज़्यादा है
http://trillian.mit.edu/~jc/humor/ATT_Copyright_true.html
trueचलाने से, अगर comment गुमराह नहीं कर रही है, कोई असर नहीं दिखताअगर कोई beginner programmer data structures और algorithms को उपयोगी तरीके से apply करने की समझ बढ़ाना चाहता है, तो क्या यहां कुछ खास देखने लायक है?
जैसे data को user space में copy न करने के लिए
read()/write()की जगहcopy_file_range()इस्तेमाल करना। यह computer science से ज़्यादा software engineering के करीब हैहो सकता है मैं इस site का point miss कर रहा हूं, लेकिन क्या हर एक के लिए पहले से man page या info page नहीं है?