On Linux BASH shell, the ‘nl’ utility is used to print out text file (or string) with line numbers. If you type in nl –help you will have a page of help information on using this program. The first two lines are:
1 2 3 | root@uploadbeta:~# nl --help | head -2 Usage: nl [OPTION]... [FILE]... Write each FILE to standard output, with line numbers added. |
root@uploadbeta:~# nl --help | head -2 Usage: nl [OPTION]... [FILE]... Write each FILE to standard output, with line numbers added.
If you do not give any command line parameter, the nl will ask input from keyboard until you hit Ctrl+C or Ctrl+D. At each line, it will print line numbers. It is noted that on Windows, Ctrl+Z is used to terminate the keyboard input (EOF) but on Linux, the shortcut is used to put the current job in the background. On Linux, Ctrl+D yields the EOF. Ctrl+C on both OS are used to break the input.
1 2 3 4 5 6 7 | root@uploadbeta:~# nl HelloACM.com 1 HelloACM.com Rocks! 2 Rocks! hello 3 hello |
root@uploadbeta:~# nl HelloACM.com 1 HelloACM.com Rocks! 2 Rocks! hello 3 hello
You can also give the text file names one by one on command line. And the line numbers will be shown continuously.
1 2 3 4 5 6 7 8 | root@uploadbeta:~# echo -e "test1\ntest2\ntest3" > test root@uploadbeta:~# nl test test 1 test1 2 test2 3 test3 4 test1 5 test2 6 test3 |
root@uploadbeta:~# echo -e "test1\ntest2\ntest3" > test root@uploadbeta:~# nl test test 1 test1 2 test2 3 test3 4 test1 5 test2 6 test3
If some file is not found, it will show the not-found message.
1 2 3 4 5 6 7 8 | root@uploadbeta:~# nl test test aa 1 test1 2 test2 3 test3 4 test1 5 test2 6 test3 nl: aa: No such file or directory |
root@uploadbeta:~# nl test test aa 1 test1 2 test2 3 test3 4 test1 5 test2 6 test3 nl: aa: No such file or directory
C++ Implementation
Now, we are going to use C++ (GCC Compiler) to make a simple implementation of the nl shown above. It is noted that the full nl supports many command line options, but we are just going to implement the basics.
The following deals with the input from the keyboard if no parameters are given.
1 2 3 4 5 6 7 8 9 10 11 | if (argc == 1) { int num = 1; while (true) { string s; getline(std::cin, s); if (cin.eof()) break; // check for Ctrl + D cout << TAB << num << TAB << s << endl; num ++; } return 0; } |
if (argc == 1) { int num = 1; while (true) { string s; getline(std::cin, s); if (cin.eof()) break; // check for Ctrl + D cout << TAB << num << TAB << s << endl; num ++; } return 0; }
To simulate the usage message is easy.
1 2 3 4 5 6 | if ((argc == 2) && (strcmp(argv[1], "--help") == 0)) { cout << "http://HelloACM.com" << endl; cout << "Usage: nl [FILE]..." << endl; cout << "Write each FILE to standard output, with line numbers added." ; return 0; } |
if ((argc == 2) && (strcmp(argv[1], "--help") == 0)) { cout << "http://HelloACM.com" << endl; cout << "Usage: nl [FILE]..." << endl; cout << "Write each FILE to standard output, with line numbers added." ; return 0; }
Now, we can treat each parameter in the command line as a input source file name and check if it can be opened. Read each file line by line and output the line number.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int num = 1; for (int i = 1; i < argc; i ++) { ifstream f(argv[i]); // open file if (f.good()) { // check if is a file and can be opened string s; while (getline(f, s)) { // read a line cout << TAB << num << TAB << s << endl; num ++; } f.close(); } else { f.close(); cout << "nl: " << argv[i] << " is not a file." << endl; } } |
int num = 1; for (int i = 1; i < argc; i ++) { ifstream f(argv[i]); // open file if (f.good()) { // check if is a file and can be opened string s; while (getline(f, s)) { // read a line cout << TAB << num << TAB << s << endl; num ++; } f.close(); } else { f.close(); cout << "nl: " << argv[i] << " is not a file." << endl; } }
The full C++ source code can be found below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include <iostream> #include <fstream> #include <string.h> using namespace std; const char TAB = 9; int main(int argc, char *argv[]) { if (argc == 1) { int num = 1; while (true) { string s; getline(std::cin, s); if (cin.eof()) break; // check for Ctrl + D cout << TAB << num << TAB << s << endl; num ++; } return 0; } if ((argc == 2) && (strcmp(argv[1], "--help") == 0)) { cout << "http://HelloACM.com" << endl; cout << "Usage: nl [FILE]..." << endl; cout << "Write each FILE to standard output, with line numbers added." ; return 0; } int num = 1; for (int i = 1; i << argc; i ++) { ifstream f(argv[i]); if (f.good()) { string s; while (getline(f, s)) { cout << TAB << num << TAB << s << endl; num ++; } f.close(); } else { f.close(); cout << "nl: " << argv[i] << " is not a file." << endl; } } return 0; } |
#include <iostream> #include <fstream> #include <string.h> using namespace std; const char TAB = 9; int main(int argc, char *argv[]) { if (argc == 1) { int num = 1; while (true) { string s; getline(std::cin, s); if (cin.eof()) break; // check for Ctrl + D cout << TAB << num << TAB << s << endl; num ++; } return 0; } if ((argc == 2) && (strcmp(argv[1], "--help") == 0)) { cout << "http://HelloACM.com" << endl; cout << "Usage: nl [FILE]..." << endl; cout << "Write each FILE to standard output, with line numbers added." ; return 0; } int num = 1; for (int i = 1; i << argc; i ++) { ifstream f(argv[i]); if (f.good()) { string s; while (getline(f, s)) { cout << TAB << num << TAB << s << endl; num ++; } f.close(); } else { f.close(); cout << "nl: " << argv[i] << " is not a file." << endl; } } return 0; }
It is noted that we can use pipe line (supported both on Linux and Windows shell) to give input to nl program. (the following BASH script is to print a square number using bc)
1 2 3 4 5 6 7 8 9 10 | root@uploadbeta:~# cat sqrt | nl 1 #!/bin/bash 2 if [ $# -ne 1 ]; then 3 echo 'Usage: sqrt number' 4 exit 1 5 else 6 echo -e "sqrt($1)\nquit\n" | bc -q -i | head -2 | tail -1 7 fi root@uploadbeta:~# |
root@uploadbeta:~# cat sqrt | nl 1 #!/bin/bash 2 if [ $# -ne 1 ]; then 3 echo 'Usage: sqrt number' 4 exit 1 5 else 6 echo -e "sqrt($1)\nquit\n" | bc -q -i | head -2 | tail -1 7 fi root@uploadbeta:~#
The pre-compiled nl (using GCC compiler on windows) can be downloaded [here] .
C Implementation
Here is another nice implementation but in pure C programming language. The main difference is the code to deal with reading from text files (I/O). The author is Kevin Guanche Darias.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <string.h> #include <inttypes.h> #define TAB '\t' #define MAX_LINE_SIZE sizeof(char) * 65536 #define EOL "\r\n" #define NLPRINT LineNumber++;printf("%c%d%c%s",TAB,LineNumber,TAB,Line) int main(int argc, char **argv){ uint32_t LineNumber = 0; char *Line = (char *)malloc(MAX_LINE_SIZE); memset(Line,0,MAX_LINE_SIZE); puts("Kevin Guanche Darias :: nl tool"); // File not specified use stdin if(argc == 1){ while(1){ fgets(Line,MAX_LINE_SIZE,stdin); if(feof(stdin)) break; NLPRINT; } return EXIT_SUCCESS; }else if(argc == 2 && (strcmp(argv[1],"--help") == 0)){ // Show help puts("Kevin Guanche Darias nl for Windows"); printf("Usage: %s [File]..."EOL,argv[0]); puts("Writes each file to standard output, with line numbers added"); return EXIT_SUCCESS; } FILE *FilePointer; int i; for(i = 1; i < argc;(i++)){ FilePointer = fopen(argv[i],"r"); if(FilePointer != NULL){ while(!feof(FilePointer)){ fgets(Line,MAX_LINE_SIZE,FilePointer); NLPRINT; } }else{ // File Error printf("%s: %s is not a file "EOL,argv[0],argv[i]); } fclose(FilePointer); } return EXIT_SUCCESS; } |
#include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <string.h> #include <inttypes.h> #define TAB '\t' #define MAX_LINE_SIZE sizeof(char) * 65536 #define EOL "\r\n" #define NLPRINT LineNumber++;printf("%c%d%c%s",TAB,LineNumber,TAB,Line) int main(int argc, char **argv){ uint32_t LineNumber = 0; char *Line = (char *)malloc(MAX_LINE_SIZE); memset(Line,0,MAX_LINE_SIZE); puts("Kevin Guanche Darias :: nl tool"); // File not specified use stdin if(argc == 1){ while(1){ fgets(Line,MAX_LINE_SIZE,stdin); if(feof(stdin)) break; NLPRINT; } return EXIT_SUCCESS; }else if(argc == 2 && (strcmp(argv[1],"--help") == 0)){ // Show help puts("Kevin Guanche Darias nl for Windows"); printf("Usage: %s [File]..."EOL,argv[0]); puts("Writes each file to standard output, with line numbers added"); return EXIT_SUCCESS; } FILE *FilePointer; int i; for(i = 1; i < argc;(i++)){ FilePointer = fopen(argv[i],"r"); if(FilePointer != NULL){ while(!feof(FilePointer)){ fgets(Line,MAX_LINE_SIZE,FilePointer); NLPRINT; } }else{ // File Error printf("%s: %s is not a file "EOL,argv[0],argv[i]); } fclose(FilePointer); } return EXIT_SUCCESS; }
The malloc() part can obviously be optimized. The pre-compiled exe can be downloaded from the Author’s website.
If you prefer a local download, that is also possible: click to download from local server
–EOF (The Ultimate Computing & Technology Blog) —
loading...
Last Post: Less Known HTML Tags
Next Post: Coding Exercise - Source Codes and Binaries