標籤: 南投搬家公司費用

  • Windows終端利器Cmder

    Windows終端利器Cmder

    在IT這一行,大部分情況下都是推薦大家使用Linux或者類Unix操作系統去編程,Linux作為一代優秀的操作系統,已經人盡皆知,在IT行業已經成為核心。有條件的大佬都選擇了使用mac編程,最優秀的莫過於終端體驗了,與Linux完全一致的命令行,帶來了許許多多的方便,但是使用Windows的用戶呢?相信大家都使用過cmd終端,它到底好不好呢。相信大家心中已經有了評判。

    一、為什麼要換成cmder

    現在我就要推薦一款Windows下的終端—>cmder
    先來上兩張圖給大家看看

    都不用我說,一眼就能分辨出他倆的區別了,其實他倆最大的區別是cmder完全支持Linux命令行,包括vi,而且可以多開,快捷鍵複製粘貼,分屏等,功能非常強大

    二、下載和安裝

    1.下載

    官網自己下載也可以

    • 在官網下載的時候有兩個版本
      • Mini版本,只有簡單的命令行
      • Full版本,包含git功能(分佈式版本控制系統的git)

    我推薦大家安裝Full版本,這樣就可以不用單獨安裝git了

    2.安裝

    安裝非常簡單,下載完成后,直接解壓到你存放軟件的目錄就好了

    然後雙擊一下cmder.exe就可以先簡單體驗一下了

    三、個性化設置

    這款軟件可以完全替代Windows系統自帶的cmd終端,當然需要一點人性化的設置

    1.配置環境變量

    我就只上圖了,環境變量配置太過簡單了,百度上太多了,都是通用套路,配置完環境變量,就可以直接在Windows+r鍵里運行cmder打開終端了

    上圖中我把git也配置進去了,這樣就不會說git不是內部或者外部命令了

    2.配置右鍵菜單啟動

    右鍵管理員身份運行cmder.exe,然後把下面的命令複製到cmder中執行一次

    // 設置任意地方鼠標右鍵啟動Cmder
    Cmder.exe /REGISTER ALL

    3.進入設置的方法

    右下角的,然後選擇Settings或者直接使用快捷鍵Windows+Alt+p打開設置

    如果不習慣英文,可以將設置改成中文

    下次再次打開設置,又會中文,只有這個設置生效一次,其他的都可以永久生效

    4.設置字體風格等

    設置字體的風格,大小等,圖中紅色位置不要勾選,否則會出現cmder終端字體重疊錯位的問題

    終端界面的字體大小在設置里可以修改,也可以在終端界面滑動鼠標滾輪,或者觸控板雙擊縮放調整字體大小

    5.窗口位置大小記憶

    勾選這兩個設置,只需要設置一次,下次會自動記住上次終端在桌面出現的位置和窗口大小

    6.設置vi模式下ESC鍵最小化窗口的問題

    • 將圖中紅色改成除了總是的其他選項,否則使用vi時會出現無法切換模式的問題
    • 勾選綠色的選項可以解決打開多個終端,任務欄显示多個窗口的問題

    7.解決中文亂碼的問題

    在使用ls命令時,中文亂碼的解決方案,將下面的代碼複製到圖中位置

    set LANG=zh_CN.UTF-8
    set LC_ALL=zh_CN.utf8

    8.強製作為默認終端

    • 圖中綠色設置可以強制將cmder註冊成Windows的默認終端

      設置此選項后,系統啟動后就會生效,且,即使你打開的是cmd,也會被放到cmder的窗口中執行

    • 紅色選項可以解決每次關閉控制台時,彈出確認關閉的彈窗

    9.解決粘貼多行文本時的彈窗

    例如在終端中執行多行SQL語句,總會彈出提示,勾選選項可以解決

    10.將命令提示改成$

    默認的命令提示符是λ,大家都知道Linux是$,這裏提供一下修改的方法,並不是必須的

    1)首先在cmder的安裝目錄下,找到vendor/目錄,然後找到clink.lua文件

    2)右鍵使用sublime打開

    • 沒有sublime或者notepad++打開也可以,還沒有的話,記事本也可以的

    3)打開后可以Ctrl+F查找下面的字段
    local lambda =
    4)將local lambda =""的值替換成$

    5)保存關閉,重啟終端

    11.將Idea的Terminal終端換成cmder

    1)在idea中打開其他設置界面,如圖所示

    在idea中settings是對當前項目生效,Other Settings是對所有項目生效

    2)如圖中修改shell Path的路徑,替換成下面的內容

    注意將cmder安裝目錄換成你的安裝目錄

    //這種方式比較可靠,避免了環境變量失效的問題
    "cmd.exe" /k ""你的cmder安裝目錄\vendor\init.bat""
    
    //或者,這個需要有環境變量
    "cmd.exe" /k ""%環境變量配置的cmder home目錄名稱%\vendor\init.bat""

    3)再次打開Terminal終端就可以使用Linux命令了

    12.將vscode的Terminal終端設置成cmder

    1)打開設置

    2)搜索code save,點擊打開設置json文件

    3)將下面的代碼粘貼到文件中,修改為自己需要的內容

    注意修改cmder的安裝目錄為自己的安裝目錄

    // 設置終端為cmder
    "terminal.integrated.shell.windows": "cmd.exe",
    "terminal.integrated.env.windows": {
        //設置cmder的根目錄
        "CMDER_ROOT": "cmder的根目錄"
    },
    "terminal.integrated.shellArgs.windows": [
        "/k",
        //設置啟動初始化目錄
        "cmder的根目錄\\vendor\\init.bat"
    ],
    
    //下面的設置可以不需要
    //終端顏色配置
    "workbench.colorCustomizations": {
        //可以將鼠標放到下面的色號上根據自己的偏好進行選擇
        "terminal.foreground": "#37FF13",
        "terminal.background": "#2b2424"
    },
    "terminal.integrated.cursorBlinking": true,
    //設置terminal中的行高
    "terminal.integrated.lineHeight": 1.1,
    "terminal.integrated.letterSpacing": 0.1,
    "terminal.integrated.fontSize": 12, //字體大小設置
    "terminal.integrated.fontFamily": "monaco", //字體設置
    "terminal.integrated.shell.linux": "/bin/zsh"

    4)Ctrl+J打開終端,就可以使用了

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 印尼水患災情慘重 雅加達省長遭民眾控告

    摘錄自2020年1月14日中央社報導

    印尼一名律師今天(14日)表示,在暴雨引發洪水及土石流災情,導致數十人喪命、數千人無家可歸後,雅加達省長阿尼斯(AniesBaswedan)因此被這座大城市的居民控告。

    超過200名水患受災民眾昨天在首都雅加達(Jakarta)地方法院提出集體訴訟,尋求總計約430億印尼盾(約新台幣9000萬元)的賠償金。

    這起訴訟指出,阿尼斯未能替雅加達這座大型城市提供合適的預警系統及有效的緊急救難措施,好讓人民的性命及財務損失降到最低。

    雅加達的法務局沒有立即回應置評請求。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 富士康杭州投12億,發展新能源汽車租賃業務

    富士康集團總投資12億元,註冊資本1億元,成立杭州浙譽新能源汽車服務有限公司。作為富士康科技集團在杭州開展新能源汽車分時租賃運營的服務主體,專案計畫三年內在杭州地區累計投放5000輛電動汽車。   電動汽車分時租賃是一種城市短途出行方式,在汽車共用的基礎上使用電動汽車進行共用運營,通過會員制的方式以小時計費的新能源電動汽車的租賃服務。其租賃模式就像“租公共自行車一樣”,會員通過網路或手機終端輕鬆實現汽車預定,然後線上下取車實現消費,整個過程全程自助。   該專案是2015年3月富士康集團與杭州市政府簽署全面戰略合作協議後,與杭州進行全面深化合作所邁出的實質性第一步。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 二叉搜索樹BST(C語言實現可用)

    二叉搜索樹BST(C語言實現可用)

    1:概述

    搜索樹是一種可以進行插入,搜索,刪除等操作的數據結構,可以用作字典或優先級隊列。二叉搜索樹是最簡單的搜索樹。其左子樹的鍵值<=根節點的鍵值,右子樹的鍵值>=根節點的鍵值。

    如果共有n個元素,那麼每次操作需要的O(log n)的時間.

     

     

     

    常用知識點

    • 滿二叉樹 : 一棵深度為k,且有2^k-1個節點的二叉樹,稱為滿二叉樹。這種樹的特點是每一層上的節點數都是最大節點數。
    • 完全二叉樹 : 而在一棵二叉樹中,除最後一層外,若其餘層都是滿的,並且最後一層要麼是滿的,要麼在右邊缺少連續若干節點,則此二叉樹為完全二叉樹。具有n個節點的完全二叉樹的深度為floor(log2n)+1。深度為k的完全二叉樹,至少有2^(k-1)個恭弘=叶 恭弘子節點,至多有2^k-1個節點。

    2.基本操作

    1. 查找(search)
    2. 插入(insert)
    3. 刪除(remove)

    3:操作原理

      

    查找

    假設查找的值為x,從根節點的值開始比對,如果小於根節點的值,則往左兒子繼續查找,如果大於根節點的值,則往右兒子繼續查找.依次類推.直到當前節點的值等於要查找的值.

     

      以查找數值10為例

    插入

    按照查找的步驟即可找到插入值應該在的位置

     

     

    以插入數值6為例

    刪除:

    有四種情況:

    1: // 當前節點無左節點 ,右字節點7覆蓋5, 

    : 3: // 當前節點無右節點 ,右字節點7覆蓋5, 

     

     : 4: // 刪除節點5的左節點沒有右節點, 只需要8作為3的右節點 ,3節點覆蓋5

     

     

    : 2:  如果以上3中情況都沒有,只需要尋找當前節點的左節點的所有字節點的最大值,用最大值填充5節點 4填充5

     

     

     

     

    5:完整代碼

    #include <stdio.h> 
    #include <stdlib.h>
    struct TNode{
        int data;
        struct TNode *lt;
        struct TNode *rt;    
    };
    struct TNode* insrtTree(struct TNode *t,int key,int i);
    void printTree(struct TNode *root);
    struct TNode* delTree(struct TNode* t,int key);
    int find(struct TNode* t,int key); 
    int arr[1000]={0};
    int main(){
        int n,m;
        int i,t;
        scanf("%d%d",&n,&m); 
        struct TNode *root=NULL;
        for(i=0;i<n;i++){
            scanf("%d",&arr[i]); 
            root=insrtTree(root,arr[i],i);
        }
        //t=arr[m-1];
        
        /*
        if(arr[m-1]==0){
            printf("Right child");
        }else{
            printf("Light child");
        }*/
        root=delTree(root,10);
        printTree(root);
        return 0;
    }
    
    int find(struct TNode* pt,int key){
        if(pt==NULL)return NULL;
        else if(pt->data==key)return 1;
        else if(pt->data>key) return find(pt->lt,key);
        else if(pt->data<key) return find(pt->rt,key);
    }
    // 刪除節點 
    struct TNode* delTree(struct TNode* pt,int key){
        if(pt==NULL)return NULL;
        else if(pt->data>key) pt->lt=delTree(pt->lt,key);//尋找左節點 
        else if(pt->data<key) pt->rt=delTree(pt->rt,key);//尋找右節點
        //  找到節點 處理四種情況  
        else if(pt->lt==NULL){ // 當前節點無左節點 
            struct TNode* curt=pt->rt;
            free(pt);
            return curt;
        }else if(pt->rt==NULL){// 當前節點無右節點 
            struct TNode* curt=pt->lt;
            free(pt);
            return curt;
        }else if(pt->lt->rt==NULL){// 當前節點的左節點無右節點 
            struct TNode* curt=pt->lt;
            curt->rt=pt->rt;
            free(pt);
            return curt;
        }else{ 
        // 以上不滿足就把左兒子的子孫中最大的節點, 即右子樹的右子樹的...右子樹, 
        //提到需要刪除的節點位置
                struct TNode* p;
                for(p=pt->lt;p->rt->rt!=NULL;p=p->rt);
                struct TNode* curt=p->lt;
                p->rt=curt->rt;
                curt->lt=pt->lt;
                curt->rt=pt->rt;
                free(p);
                return curt;
        }
        return pt;
    }
    struct TNode* insrtTree(struct TNode *t,int key,int i){
        if(t==NULL){ //處理第一個節點 以及子節點為NULL情況 
            t=(struct TNode*)malloc(sizeof(struct TNode));
            t->lt=t->rt=NULL;
            t->data=key;
            return t;
        }
        if(t->data>key){// 插入左子樹情況 
             arr[i]=1;
            t->lt=insrtTree(t->lt,key,i);
        }else{         // 插入右子樹情況
            arr[i]=0;
            t->rt=insrtTree(t->rt,key,i);
        }
        return t;
    }
    void printTree(struct TNode *root){
        if(root==NULL)return;
        printf("%d ",root->data);
        printTree(root->lt);
        printTree(root->rt);
    }

     

    說明: 本身學習了 https://blog.csdn.net/li_l_il/article/details/88677927 但是完善了代碼 

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 數據結構之隊列and棧總結分析

    一、前言:

      數據結構中隊列和棧也是常見的兩個數據結構,隊列和棧在實際使用場景上也是相輔相成的,下面簡單總結一下,如有不對之處,多多指點交流,謝謝。

    二、隊列簡介

      隊列顧名思義就是排隊的意思,根據我們的實際生活不難理解,排隊就是有先後順序,先到先得,其實在程序數據結構中的隊列其效果也是一樣,及先進先出。

         隊列大概有如下一些特性:

         1、操作靈活,在初始化時不需要指定其長度,其長度自動增加(默認長度為32)

            注:在實際使用中,如果事先能夠預估其長度,那麼在初始化時指定長度,可以提高效率

            2、泛型的引入,隊列在定義時可以指定數據類型避免裝箱拆箱操作

         3、存儲數據滿足先進先出原則

           

       c#中有關隊列的幾個常用方法:

      • Count:Count屬性返回隊列中元素個數。
      • Enqueue:Enqueue()方法在隊列一端添加一個元素。
      • Dequeue:Dequeue()方法在隊列的頭部讀取和刪除元素。如果在調用Dequeue()方法時,隊列中不再有元素,就拋出一個InvalidOperationException類型的異常。
      • Peek:Peek()方法從隊列的頭部讀取一個元素,但不刪除它。
      • TrimExcess:TrimExcess()方法重新設置隊列的容量。Dequeue()方法從隊列中刪除元素,但它不會重新設置隊列的容量。要從隊列的頭部去除空元素,應使用TrimExcess()方法。
      • Clear:Clear()方法從隊列中移除所有的元素。
      • ToArray:ToArray()複製隊列到一個新的數組中。

      下面通過隊列來實例模擬消息隊列的實現流程:

     

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace dataStructureQueueTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("通過Queue來模擬消息隊列的實現");
                QueueTest queueTest = new QueueTest();
    
                while (true)
                {
                    Console.WriteLine("請輸入你操作的類型:1:代表生成一條消息,2:代表消費一條消息");
                    string type = Console.ReadLine();
                    if (type == "1")
                    {
                        Console.WriteLine("請輸入具體消息:");
                        string inforValue = Console.ReadLine();
                        queueTest.InformationProducer(inforValue);
                    }
                    else if (type == "2")
                    {
                        //// 在消費消息的時候,模擬一下,消費成功與消費失敗下次繼續消費的場景
    
                        object inforValue = queueTest.InformationConsumerGet();
                        if (inforValue == null)
                        {
                            Console.WriteLine("當前無可消息可消費");
                        }
                        else
                        {
                            Console.WriteLine("獲取到的消息為:" + inforValue);
    
                            Console.WriteLine("請輸入消息消費結果:1:成功消費消息,2:消息消費失敗");
                            string consumerState = Console.ReadLine();
    
                            ///// 備註:該操作方式線程不安全,在多線程不要直接使用
                            if (consumerState == "1")
                            {
                                queueTest.InformationConsumerDel();
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("操作有誤,請重新選擇");
                    }
                }
            }
        }
    
        /// <summary>
        /// 隊列練習
        /// </summary>
        public class QueueTest
        {
            /// <summary>
            /// 定義一個隊列
            /// </summary>
            public Queue<string> queue = new Queue<string>();
    
            /// <summary>
            /// 生成消息--入隊列
            /// </summary>
            /// <param name="inforValue"></param>
            public void InformationProducer(string inforValue)
            {
                queue.Enqueue(inforValue);
            }
    
            /// <summary>
            /// 消費消息---出隊列--只獲取數據,不刪除數據
            /// </summary>
            /// <returns></returns>
            public object InformationConsumerGet()
            {
                if (queue.Count > 0)
                {
                    return queue.Peek();
                }
    
                return null;
            }
    
            /// <summary>
            /// 消費消息---出隊列---獲取數據的同時刪除數據
            /// </summary>
            /// <returns></returns>
            public string InformationConsumerDel()
            {
                if (queue.Count > 0)
                {
                    return queue.Dequeue();
                }
    
                return null;
            }
        }
    }

     

     

    三、棧簡介

      棧和隊列在使用上很相似,只是棧的數據存儲滿足先進后出原則,棧有如下一些特性:

         1、操作靈活,在初始化時不需要指定其長度,其長度自動增加(默認長度為10)

            注:在實際使用中,如果事先能夠預估其長度,那麼在初始化時指定長度,可以提高效率

            2、泛型的引入,棧在定義時可以指定數據類型避免裝箱拆箱操作

         3、存儲數據滿足先進后出原則

        c#中有關棧的幾個常用方法:

    • Count:Count屬性返回棧中的元素個數。
    • Push:Push()方法在棧頂添加一個元素。
    • Pop:Pop()方法從棧頂刪除一個元素,並返回該元素。如果棧是空的,就拋出一個InvalidOperationException類型的異常。
    • Peek:Peek()方法返回棧頂的元素,但不刪除它。
    • Contains:Contains()方法確定某個元素是否在棧中,如果是,就返回true。

         下面通過一個棧來模擬瀏覽器的回退前進操作的實現

     

    using System;
    using System.Collections.Generic;
    
    namespace dataStructureStackTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                //// 通過棧來模擬瀏覽器回退前進操作
                ////   1、定義兩個棧,分別記錄回退的地址集合,和前進地址集合
                ////   2、在操作具體的回退或者前進操作時
                ////      如果和前一次操作相同,那麼就取出對應隊列的一條數據存儲到另外一個隊列
                Console.WriteLine("本練習模擬瀏覽器的回退前進操作:");
    
                /// 假設瀏覽器已瀏覽了20個網站記錄
                StackTest stackTestBack = new StackTest(20);
                StackTest stackTestGo = new StackTest(20);
                for (int i = 0; i < 20; i++)
                {
                    stackTestBack.PushStack("網站" + (i + 1).ToString());
                }
    
                //// 記錄上一次操作
                string beforOpert = "";
                while (true)
                {
                    Console.WriteLine("");
                    Console.WriteLine("請輸入你操作的類型:1:回退,2:前進");
                    string type = Console.ReadLine();
    
                    if (type == "1")
                    {
                        //// 出棧
                        if (beforOpert == type)
                        {
                            stackTestGo.PushStack(stackTestBack.GetAndDelStack());
                        }
                        string wbeSit = stackTestBack.GetStack();
                        Console.WriteLine("回退到頁面:" + wbeSit);
                        beforOpert = type;
                    }
                    else if (type == "2")
                    {
                        //// 出棧
                        if (beforOpert == type)
                        {
                            stackTestBack.PushStack(stackTestGo.GetAndDelStack());
                        }
                        string wbeSit = stackTestGo.GetStack();
    
                        Console.WriteLine("回退到頁面:" + wbeSit);
                        beforOpert = type;
                    }
                    else
                    {
                        Console.WriteLine("請輸入正確的操作方式!!");
                    }
                }
            }
        }
    
        /// <summary>
        /// 隊列練習
        /// </summary>
        public class StackTest
        {
            /// <summary>
            /// 定義一個棧
            /// </summary>
            public Stack<string> stack;
    
            /// <summary>
            ///無參數構造函數,棧初始化為默認長度
            /// </summary>
            public StackTest()
            {
                stack = new Stack<string>();
            }
    
            /// <summary>
            ///有參數構造函數,棧初始化為指定長度
            ///如果在定義隊列時,如果知道需要存儲的數據長度,那麼最好預估一個長度,並初始化指定的長度
            /// </summary>
            public StackTest(int stackLen)
            {
                stack = stackLen > 0 ? new Stack<string>(stackLen) : new Stack<string>();
            }
    
            /// <summary>
            /// 入棧
            /// </summary>
            /// <param name="inforValue"></param>
            public void PushStack(string inforValue)
            {
                stack.Push(inforValue);
            }
    
            /// <summary>
            /// 出棧(但不刪除)
            /// </summary>
            /// <returns></returns>
            public string GetStack()
            {
                if (stack.Count > 0)
                {
                    return stack.Peek();
                }
    
                return null;
            }
    
            /// <summary>
            /// 出棧(並刪除)
            /// </summary>
            /// <returns></returns>
            public string GetAndDelStack()
            {
                if (stack.Count > 0)
                {
                    return stack.Pop();
                }
    
                return null;
            }
        }
    }

     

    四、使用場景總結

      根據隊列和棧的特點,下面簡單總結一下隊列和棧的一些實際使用場景

       隊列:

        1、異步記錄日誌,此處會涉及到單例模式的使用

        2、消息隊列

        3、業務排隊,比如12306車票購買排隊等候

        4、其他符合先進先出原則的業務操作

       棧:

        1、可回退的操作記錄,比如:瀏覽器的回退操作

        2、計算表達式匹配,比如:計算器表達式計算

        3、其他符合先進后出原則的業務操作

     

    附件:

    關於這一些練習的代碼,上傳到github,有興趣的可以看一下:

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 吉利:到2020年新能源汽車銷量將實現90%以上的占比

    日前,吉利控股集團董事長李書福在接受《中國經營報》記者採訪時表示,以後我們開發的新產品基本是新能源汽車和智慧互聯汽車,傳統汽車就逐漸不生產了。

    此話背後,實際就是吉利於近日提出的“藍色吉利行動”中的重要一環,到2020年,其新能源汽車銷量將實現90%以上的銷量占比。

    具體來說,根據“藍色吉利行動”,吉利新能源將在技術上主打純電動、油電混動、插電式混合動力三種路線,並通過兩大平臺——FE(中高端)與PE(緊湊級)平臺來發展純電動車板塊。

    從以上路徑上看,吉利與其他車企並無二致,但其公佈的“五大承諾”卻足以讓業內熱議——第一,提前全面實現2020年國家第四階段每百公里5.0L的企業平均燃油消耗限值;第二,實現消費者用傳統汽車的購買成本購買插電式混動汽車的夢想;第三,實現到2020年新能源汽車銷量占吉利整體銷量的90%以上;第四,在氫燃料及金屬燃料電池汽車研發方面取得實質性成果;第五,實現新能源技術,智慧化、輕量化技術在行業的領先地位。

    雖然言之鑿鑿,但不得不說的是,90%以上的新能源銷量加上此前吉利發佈的“2020年實現120萬輛”的銷量目標,同年吉利新能源汽車銷量目標竟逾100萬輛。

    對此,吉利控股集團總裁CEO安聰慧表示:“吉利制定這樣的目標並不是為了和其他企業進行對比,而是結合自身發展提出來的。”

    據其介紹,在技術領域,吉利汽車將以與沃爾沃合作打造的CMA中高級車基礎模組架構為核心打造新能源車型,該方面的設計研發工作主要由吉利汽車歐洲研發中心承擔,該中心在瑞典哥德堡已有1200名工程師,中國杭州也有300名工程師,負責架構開發、上車體開發、核心部件開發開發,整車設計、工程製造及新技術的研發。CMA基礎模組架構可以實現電機+發動機等核心部件的批量生產。

    此外,2015年初,吉利與新大洋機電集團成立合資公司並推出知豆電動車,加之此前其子公司上海華普國潤與康迪車業成立的合資公司,署名吉利旗下的新能源車型並不止吉利品牌。雖然,吉利在新能源領域的開疆破土頗有“借力而為”之感,但不論如何,2015年1~11月,在乘聯會統計的自主品牌新能源車銷量占比情況中,吉利節節攀升,的確實現了在自主品牌領域的市占率穩增,也正因如此,加之政策的多重鼓勵,吉利才許下到2020年實現新能源汽車逾百萬的戰略目標。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 源碼學習系列之SpringBoot自動配置(篇二)

    源碼學習系列之SpringBoot自動配置(篇二)

    源碼學習系列之SpringBoot自動配置(篇二)之HttpEncodingAutoConfiguration 源碼分析

    繼上一篇博客之後,本博客繼續跟一下SpringBoot的自動配置源碼

    ok,先複習一下上一篇的內容,從前面的學習,我們知道了SpringBoot的自動配置主要是由一個選擇器AutoConfigurationImportSelector,先通過選擇器將自動配置的類加載到Spring容器

    注意點:

    • List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);獲取的候選配置的類名
    • 由SpringFactoriesLoader加載器負責加載配置類名,已經裝載配置類到容器,SpringFactoriesLoader的loadSpringFactories方法讀取自動配置工程的META-INF/spring.factories配置文件,加載配置類的全類名,包裝成Properties對象,然後再加載到容器里
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
        /* 將spring.factories的類都裝載到Spring容器*/
         public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
    
            try {
            //將META-INF/spring.factories文件里配置的屬性都裝載到Enumeration數據結構里
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                ArrayList result = new ArrayList();
                //遍歷獲取屬性,然後再獲取對應的配置類全類名
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);
                    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                }
    
                return result;
            } catch (IOException var8) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
            }
        }
    

    ok,Springboot的自動配置類都在這個包里,源碼很多,所以本博客只是簡單跟一下源碼

    自動配置可以說是SpringBoot框架的一個很重要的功能,其強大的功能就是通過很多配置類實現的,當然這麼多配置,可以參考SpringBoot官方的配置參考:
    https://docs.spring.io/spring-boot/docs/2.1.10.RELEASE/reference/html/common-application-properties.html

    SpringBoot的自動配置類很多,顯然不是每個配置類都生效的,比如你沒引對應的jar,那對應的配置類肯定是不起效的,ok,本博客以HttpEncodingAutoConfiguration自動編碼配置類為實例,記錄一下SpringBoot的自動配置

    先補充一些@Conditional註解的用法:詳情可以參考我上篇博客

    @Conditional派生註解 作用(都是判斷是否符合指定的條件)
    @ConditionalOnJava 系統的java版本是否符合要求
    @ConditionalOnBean 有指定的Bean類
    @ConditionalOnMissingBean 沒有指定的bean類
    @ConditionalOnExpression 符合指定的SpEL表達式
    @ConditionalOnClass 有指定的類
    @ConditionalOnMissingClass 沒有指定的類
    @ConditionalOnSingleCandidate 容器只有一個指定的bean,或者這個bean是首選bean
    @ConditionalOnProperty 指定的property屬性有指定的值
    @ConditionalOnResource 路徑下存在指定的資源
    @ConditionalOnWebApplication 系統環境是web環境
    @ConditionalOnNotWebApplication 系統環境不是web環境
    @ConditionalOnjndi JNDI存在指定的項

    通過上篇博客的學習,我們已經知道了SpringBoot有很多自動配置類,所以本博客拿HttpEncodingAutoConfiguration類來看看

    補充:

    • @Configuration proxyBeanMethods屬性:默認是開啟的,開啟后允許其它配置類調用這個類的@bean方法,詳情參看Spring官方文檔:
    package org.springframework.boot.autoconfigure.web.servlet;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
    import org.springframework.boot.autoconfigure.http.HttpProperties;
    import org.springframework.boot.autoconfigure.http.HttpProperties.Encoding;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
    import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.web.filter.CharacterEncodingFilter;
    
    @Configuration(
        proxyBeanMethods = false
    )//指定是一個配置列,關了proxyBeanMethods,其它配置類就不能調相應的@bean類
    @EnableConfigurationProperties({HttpProperties.class})//讓使用 @ConfigurationProperties註解的HttpProperties類生效,HttpProperties類通過ConfigurationProperties註解,將屬性配置一個一個加載進來
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )//指定系統環境是Web環境配置才起效,並且指定類型是SERVLET
    @ConditionalOnClass({CharacterEncodingFilter.class})//系統有CharacterEncodingFilter過濾器類,則配置類起效,CharacterEncodingFilter類:SpringMVC中進行亂碼解決的過濾器
    @ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )//判斷配置文件是否有spring.http.encoding.enabled屬性,如果沒配置,也是默認為true的,因為配置了`matchIfMissing =true`
    public class HttpEncodingAutoConfiguration {
        //創建一個Encoding對象
        private final Encoding properties;
        // 構造函數里通過properties.getEncoding();進行屬性映射,獲取默認的配置
        public HttpEncodingAutoConfiguration(HttpProperties properties) {
            this.properties = properties.getEncoding();
        }
    
        @Bean
        @ConditionalOnMissingBean//如果系統沒有CharacterEncodingFilter類,就執行characterEncodingFilter方法
        public CharacterEncodingFilter characterEncodingFilter() {
            //重新創建一個編碼過濾器
            OrderedCharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            //設置默認的配置
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
            return filter;
        }
    
        @Bean
        public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
            return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
        }
    
        private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
            private final Encoding properties;
    
            LocaleCharsetMappingsCustomizer(Encoding properties) {
                this.properties = properties;
            }
    
            public void customize(ConfigurableServletWebServerFactory factory) {
                if(this.properties.getMapping() != null) {
                    factory.setLocaleCharsetMappings(this.properties.getMapping());
                }
    
            }
    
            public int getOrder() {
                return 0;
            }
        }
    }
    

    通過對HttpEncodingAutoConfiguration源碼的學習,可以看出,其實主要是用@Conditional及其派生註解,這些註解都是要在特定情況才會起效,起效了,才會將組件加載到Spring容器里

    ok,然後我們怎麼知道哪些配置是起效的?在SpringBoot項目里,是可以通過配置,開啟打印的,可以在application.properties加上debug=true屬性就可以

    控制台打印的Positive matches就表示有效的配置類

    console打印的Negative matches表示不起效的配置類:
    比如我的項目沒有加aop的,aop自動配置類就不起效

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • ES6學習筆記01 — 暫時性死區 ( temporal dead zone )

    參考文檔:   

           

           

    注:文中代碼僅作示意,複製運行時需要適當調整

      
    ES6 規定,如果代碼區塊中存在 let  const 命令聲明的變量,這個區塊對這些變量從一開始就形成了封閉作用域,直到聲明語句完成,這些變量才能被訪問(獲取或設置),否則會報錯ReferenceError。這在語法上稱為“暫時性死區”(英temporal dead zone,簡 TDZ),即代碼塊開始到變量聲明語句完成之間的區域。
      通過 var 聲明的變量擁有變量提升、沒有暫時性死區,作用於函數作用域:

      • 當進入變量的作用域(包圍它的函數),立即為它創建(綁定)存儲空間,立即被初始化並被賦值為 undefined   
      • 當執行到變量的聲明語句時,如果變量定義了值則會被賦值
        (function fn() {  //函數作用域開始
            console.log(temp)  //undefined
            //聲明
            var temp 
            console.log(temp)  //undefined
            //賦值
            temp = 123
            console.log(temp)  //123
        })()
        //在函數作用域外訪問
        console.log(temp)  //ReferenceError: temp is not defined

     

      通過 let 聲明的變量沒有變量提升、擁有暫時性死區,作用於塊級作用域:

      • 當進入變量的作用域(包圍它的語法塊),立即為它創建(綁定)存儲空間,不會立即初始化,也不會被賦值
      • 訪問(獲取或設置)該變量會拋出異常 ReferenceError
      • 當執行到變量的聲明語句時,如果變量定義了值則會被賦值,如果變量沒有定義值,則被賦值為undefined
            {  //函數作用域開始,TDZ開始
                console.log(temp)  //ReferenceError: temp is not defined
                //聲明
                let temp  
                console.log(temp)  //ReferenceError: Cannot access 'temp' before initialization
                //賦值
                temp = 345  //TDZ結束
                console.log(temp)  //345
                //塊級作用域結束
            }
            //在塊級作用域外訪問
            console.log(temp)  //ReferenceError: temp is not defined

     

      通過 const 聲明的常量,需要在定義的時候就賦值,並且之後不能改變,暫時性死區與 let 類似。

            {   //作用域開始,TDZ開始
                console.log(temp)  //ReferenceError: temp is not defined
                //聲明並賦值
                const temp = 789  //TDZ結束
                console.log(temp)  //789 
                //給常量賦值
                temp = 987  //TypeError: Assignment to constant variable
                //作用域結束
            }   
            //在作用域外訪問
            console.log(temp)  //ReferenceError: temp is not defined

     

      
    一句話總結:在塊級作用域中, let  const 聲明的變量、常量在聲明語句執行完成之前不能訪問(包括聲明語句本身)
      另外,容易因為 暫時性死區 而出錯的細節代碼在此不展開舉例,參考文檔中有詳情舉例。  
      附:第一次寫學習筆記,寫隨筆花費的時間比學習相關內容的時間還要長,感覺有點本末倒置,不過以後回頭看應該會覺得有所值得吧!  

      

      

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 電動摺疊腳踏車、滑板車搶攻最後一哩交通需求

    電動摺疊腳踏車、滑板車搶攻最後一哩交通需求

    傳統交通工具汽機車造成許多空氣污染與碳排放,許多廠商認為解決方案是電動車、電動機車,不過,也有人提出另一個想法,那就是何不更充分利用大眾運輸工具,只要解決捷運站到目的地的「最後一哩」交通需求即可鼓勵許多人不用自己開車、騎機車,而願意搭乘捷運。在 CES 2016 上,許多廠商都推出類似概念的產品。

    Cycle Board 推出「街道衝浪者」(Street Surfer)電動滑板三輪車,這輛滑板車的前端有兩輪,連接把手,負責轉向,較大的雙前輪也提供在人行道等不平坦路面上的穩定性,後端有一個小輪,把手可伸縮配合使用者身高,也可收疊起來方便攜帶,把手上除了剎車等基本控制,還可裝上手機。「街道衝浪者」充電一次可行駛 15 到 20 英里(24.1 到 32.2 公里),最高時速 20 英里(32.2 公里)。

    ▲ Street Surfer(Source:) Urban626 則推出 Urb-e 折疊式電動腳踏車,結構像是一把折疊刀,設計師宣稱可快速折疊,實測大約 1 秒鐘可折疊起來,便於帶電車車廂,或是裝進汽車後車廂,骨架以鋁打造,總重 35 英磅(15.876 公斤),時速最高 24 公里,充電 4 小時充飽後,可以行駛 32 公里。   由 Smart Rhino 推出的 Xcooter 同樣是電動折疊式腳踏車,以 X 形狀骨架收合,充電 3 小時可行駛 28 公里,最高時速 80 公里,重量 18 公斤。這些可折疊的電動腳踏車或許是解決捷運站到目的地交通問題的解決方案,不過售價可不便宜,Urb-e 售價 1,500 美元,將近 5 萬元新台幣,Xcooter 預售價 1,499 美元,而 2016 年 4 月正式上市時售價將為 1,799 美元,將近 6 萬元新台幣。

    (本文授權轉載自《》─〈〉)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • Ansible之playbook拓展

      一、handlers和notify結合使用觸發條件

      handlers同tasks是屬同級,相當於一個特殊任務列表,這些任務同前文說的tasks里的任務沒有本質的不同,用於當關注的資源發生變化時,才會採取一定的操作。notify此action可用於在每一個play的最後被觸發,這樣可避免多次有改變發生時都執行指定的操作,僅在所有的變化發生完成后一次性地執行指定操作,在notify中列出的操作稱為handler,換句話說當所關注的資源發生變化時notify將調用handlers中定義的操作。其中notify所在任務就是被監控的任務資源變化的任務,notify可以調用多個handlers定義的操作,一個handlers里可以定義很多任務。

    ---
    - hosts: websers
      remote_user: root
    
      tasks:
        - name: create apache group
          group: name=apache gid=80 system=yes
        - name: create apache user
          user: name=apache uid=80 group=apache system=yes shell=/sbin/nologin home=/var/www/html 
        - name: install httpd
          yum: name=httpd
        - name: copy config file
          copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
          notify: restart httpd service
    
        - name: start httpd service
          service: name=httpd state=started enabled=yes
    
      handlers:
        - name: restart httpd service
          service: name=httpd state=restarted   
    

      說明:notify后指定的名稱必須要和handlers里的任務名稱相同,如不同handlers所定義的任務將不會執行,相當於沒有notify調用handlers里的任務。

      在某些情況下,我們可能同時需要調用多個handlers,或者需要使用handlers其他handlers,ansible可以很簡單的實現這些功能,如下所示

      1)調用多個handlers

    ---
    - hosts: websers
      remote_user: root
    
      tasks:
        - name: create apache group
          group: name=apache gid=80 system=yes
        - name: create apache user
          user: name=apache uid=80 group=apache system=yes shell=/sbin/nologin home=/var/www/html 
        - name: install httpd
          yum: name=httpd
        - name: copy config file
          copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
          notify: 
            - restart httpd service
            - check httpd process
    
        - name: start httpd service
          service: name=httpd state=started enabled=yes
    
      handlers:
        - name: restart httpd service
          service: name=httpd state=restarted
        - name: check httpd process                                                                                      
          shell: /usr/bin/killall -0 httpd &> /tmp/httpd.log
    

      說明:調用多個handlers我們需要在notify中寫成列表的形式,同樣我們被觸發的任務名稱需要同handlers里的被調用的任務名稱完全相同

      2)handlers調用handlers

    ---
    - hosts: websers
      remote_user: root
    
      tasks:
        - name: create apache group
          group: name=apache gid=80 system=yes
        - name: create apache user
          user: name=apache uid=80 group=apache system=yes shell=/sbin/nologin home=/var/www/html 
        - name: install httpd
          yum: name=httpd
        - name: copy config file
          copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
          notify: restart httpd service
    
        - name: start httpd service
          service: name=httpd state=started enabled=yes
    
      handlers:
        - name: restart httpd service
          service: name=httpd state=restarted
          notify: check httpd process                                                                                    
        - name: check httpd process
          shell: /usr/bin/killall -0 httpd &> /tmp/httpd.log
    

      說明:handlers調用handlers,則直接在handlers中使用notify選項就可以。

    在使用handlers我們需要注意一下幾點:

      1)handlers只有在其所在任務被執行時才會被運行,handlers定義的任務它不會像task任務那樣,自動會從上至下依次執行,它只會被notify所在的任務發生狀態改變時才會觸發handlers 的任務執行,如果一個任務中定義了notify調用handlers,但由於條件的判斷等原因,該任務尚未執行,那麼notify調用的handlers同樣也不會執行。

      2)handlers只會在play的末尾運行一次;如果想要在一個playbook的中間運行handlers,則需要使用meta模塊來實現,如:-mate: flush_handlers

      二、playbook中變量的使用

    ansible中變量的命名規範同其他語言或系統中變量命名規則非常類似。變量名以英文大小寫字母開頭,中間可以包含下劃線和数字,ansible變量的來源有很多,具體有以下幾點:

      1)ansible setup模塊,這個模塊可以從遠程主機上獲取很多遠程主機的基本信息,它所返回的所有變量都可以直接調用,有關setup說明請參考本人博客

      2)在/etc/ansible/hosts中定義,此文件是ansible執行名時默認加載的主機清單文件,在裏面除了可定義我們要管理的主機外,我們還可以定義針對單個主機定義單獨的變量,我們把針對單獨某一台主機定義的變量叫做普通變量(也可叫做主機變量);還有一種變量它不是針對單獨一個主機,它針對某一個組裡的所有主機,我們把這種變量叫做公共組變量。主機清單中定義的變量優先級是普通變量高於公共變量。

        2.1)主機變量,可以在主機清單中定義主機時為其添加主機變量以便於在playbook中使用,如下所示

    [websers]
    192.168.0.128 http_port=80 maxRequestsPerChild=808
    192.168.0.218 http_port=81 maxRequestsPerChild=909
    

        2.2)主機組變量,組變量是指定賦予給指定組內所有主機上的在playbook中可使用的變量,如下所示

    [websers]
    192.168.0.128 http_port=80 
    192.168.0.218 http_port=81 
    [websers:vars]
    maxRequestsPerChild=909

      3)通過命令行指定變量(-e指定變量賦值,可以說多個但需要用引號引起或者一個變量用一個-e指定賦值),這種在命令行指定的優先級最高。如下所示

    ansible-playbook -e 'package_name1=httpd package_name2=nginx' test_vars.yml

      4)在playbook中定義變量,最常見的定義變量的方法是使用vars代碼塊,如下所示

    ---
    - hosts: websers
      remote_user: root
      vars:
        - abc: xxx 
        - bcd: aaa  
    

      5)在獨立的變量yml文件中定義,在playbook中使用vars_files代碼塊引用其變量文件,如下所示

    [root@test ~]#cat vars.yml 
    ---
    package_name1: vsftpd
    package_name2: nginx
    [root@test ~]#cat test_vars.yml 
    ---
    - hosts: websers
      remote_user: root
      vars_files:
        - vars.yml
      tasks:
        - name: install package1
          yum: name={{ package_name1 }}
        - name: install package2
          yum: name={{ package_name2 }}
    [root@test ~]#

      6)在role中定義,這個後續說到角色在做解釋

      變量的調用方式:第一種在playbook中使用變量需要用“{{}}”將變量括起來,表示括號里的內容是一個變量,有時用“{{  variable_name }}”才生效;第二種是ansible-playbook -e 選項指定其變量,ansible-playbook -e “hosts=www user=xxxx” test.yml

      在主機清單中定義變量的方法雖然簡單直觀,但是當所需要定義的變量有很多時,並且被多台主機使用時,這種方法顯得非常麻煩,事實上ansible的官方手冊中也不建議我們把變量直接定義到hosts文件中;在執行ansible命令時,ansible會默認會從/etc/ansible/host_vars/和/etc/ansible/group_vars/兩個目錄下讀取變量定義文件,如果/etc/ansible/下沒有以上這兩個目錄,我們可以手動創建,並且可以在這兩個目錄下創建與hosts文件中的主機名或主機組同名的文件來定義變量。比如我們要給192.168.0.218 這個主機定義個變量文件,我們可以在/etc/ansible/host_vars/目錄下創建一個192.168.0.218的空白文件,然後在文件中以ymal語法來定義所需變量即可。如下所示

    [root@test ~]#tail -6 /etc/ansible/hosts 
    ## db-[99:101]-node.example.com
    [websers]
    192.168.0.128 
    192.168.0.218 
    [appsers]
    192.168.0.217
    [root@test ~]#cat /etc/ansible/host_vars/192.168.0.218 
    ---
    file1: abc
    file2: bcd
    [root@test ~]#cat test.yml 
    ---
    - hosts: 192.168.0.218
      remote_user: root
      
      tasks:
        - name: touch file1
          file: name={{ file1 }} state=touch
        - name: toch file2
          file: name={{ file2 }} state=touch
    [root@test ~]#ansible-playbook test.yml 
    
    PLAY [192.168.0.218] ************************************************************************************************
    
    TASK [Gathering Facts] **********************************************************************************************
    ok: [192.168.0.218]
    
    TASK [touch file1] **************************************************************************************************
    changed: [192.168.0.218]
    
    TASK [toch file2] ***************************************************************************************************
    changed: [192.168.0.218]
    
    PLAY RECAP **********************************************************************************************************
    192.168.0.218              : ok=3    changed=2    unreachable=0    failed=0   
    
    [root@test ~]#ansible 192.168.0.218 -m shell -a 'ls -l /root'
    192.168.0.218 | SUCCESS | rc=0 >>
    總用量 12
    -rw-r--r--. 1 root   root    0 11月 17 16:49 abc
    -rw-r--r--. 1 root   root    0 11月 17 16:49 bcd
    drwxr-xr-x. 2 qiuhom root 4096 11月 11 19:18 scripts
    drwxr-xr-x. 3 qiuhom root 4096 11月 11 19:28 test
    -rw-r--r--. 1 root   root   57 11月 13 19:15 test_cron_file
    
    [root@test ~]#
    

      說明:可看到我們定義在/etc/ansible/host_vars/下的主機變量文件中的變量生效了。

    同理,我們要想針對某個組的主機定義一些變量,我們只需要在/etc/ansible/group_vars/目錄下創建與主機清單中的主機組同名的文件即可。

      三、使用高階變量

      對於普通變量,例如由ansible命令行設定的,hosts文件中定義的以及playbook中定義的和變量文件中定義的,這些變量都被稱為普通變量或者叫簡單變量,我們可以在playbook中直接用雙大括號加變量名來讀取變量內容;除此以外ansible還有數組變量或者叫做列表變量,如下所示:

    [root@test ~]#cat vars.yml 
    ---
    packages_list:
      - vsftpd
      - nginx
    [root@test ~]#
    

      列表定義完成后我們要使用其中的變量可以列表名加下標的方式去訪問,有點類似shell腳本里的數組的使用,如下所示

    [root@test ~]#cat test.yml 
    ---
    - hosts: 192.168.0.218
      remote_user: root
      
      vars_files:
        - vars.yml
      tasks:
        - name: touch file
          file: name={{ packages_list[0] }} state=touch
        - name: mkdir dir
          file: name={{ packages_list[1] }} state=directory
    [root@test ~]#
    

      說明:我們要使用列表中的第一個元素變量,我們可以寫成vars_list[0],使用第二個變量則下標就是1,依此類推

    [root@test ~]#ansible *218 -m shell -a 'ls -l /root'
    192.168.0.218 | SUCCESS | rc=0 >>
    總用量 12
    -rw-r--r--. 1 root   root    0 11月 17 16:49 abc
    -rw-r--r--. 1 root   root    0 11月 17 16:49 bcd
    drwxr-xr-x. 2 qiuhom root 4096 11月 11 19:18 scripts
    drwxr-xr-x. 3 qiuhom root 4096 11月 11 19:28 test
    -rw-r--r--. 1 root   root   57 11月 13 19:15 test_cron_file
    
    [root@test ~]#ansible-playbook test.yml 
    
    PLAY [192.168.0.218] ************************************************************************************************
    
    TASK [Gathering Facts] **********************************************************************************************
    ok: [192.168.0.218]
    
    TASK [touch file] ***************************************************************************************************
    changed: [192.168.0.218]
    
    TASK [mkdir dir] ****************************************************************************************************
    changed: [192.168.0.218]
    
    PLAY RECAP **********************************************************************************************************
    192.168.0.218              : ok=3    changed=2    unreachable=0    failed=0   
    
    [root@test ~]#ansible *218 -m shell -a 'ls -l /root'
    192.168.0.218 | SUCCESS | rc=0 >>
    總用量 16
    -rw-r--r--. 1 root   root    0 11月 17 16:49 abc
    -rw-r--r--. 1 root   root    0 11月 17 16:49 bcd
    drwxr-xr-x. 2 root   root 4096 11月 17 17:23 nginx
    drwxr-xr-x. 2 qiuhom root 4096 11月 11 19:18 scripts
    drwxr-xr-x. 3 qiuhom root 4096 11月 11 19:28 test
    -rw-r--r--. 1 root   root   57 11月 13 19:15 test_cron_file
    -rw-r--r--. 1 root   root    0 11月 17 17:23 vsftpd
    
    [root@test ~]#
    

      說明:可看到我們創建的文件和目錄在目標主機已經生成

    上面的用法是典型的python列表的用法,在python中讀取列表中的元素就是用下標的表示來讀取相應的元素的值。接下我們將介紹另外一種更為複雜的變量,它類似python中的字典概念,但比字典的維度要高,更像是二維字典。ansible內置變量ansible_eth0就是這樣一種,它用來保存遠端主機上面eth0接口的信息,包括ip地址和子網掩碼等。如下所示

    [root@test ~]#cat test.yml     
    ---
    - hosts: 192.168.0.218
      remote_user: root
      
      tasks:
        - debug: var=ansible_eth0 
    [root@test ~]#ansible-playbook test.yml 
    
    PLAY [192.168.0.218] ************************************************************************************************
    
    TASK [Gathering Facts] **********************************************************************************************
    ok: [192.168.0.218]
    
    TASK [debug] ********************************************************************************************************
    ok: [192.168.0.218] => {
        "ansible_eth0": {
            "active": true, 
            "device": "eth0", 
            "features": {
                "fcoe_mtu": "off [fixed]", 
                "generic_receive_offload": "on", 
                "generic_segmentation_offload": "on", 
                "highdma": "off [fixed]", 
                "large_receive_offload": "off [fixed]", 
                "loopback": "off [fixed]", 
                "netns_local": "off [fixed]", 
                "ntuple_filters": "off [fixed]", 
                "receive_hashing": "off [fixed]", 
                "rx_checksumming": "on", 
                "rx_vlan_filter": "on [fixed]", 
                "rx_vlan_offload": "on [fixed]", 
                "scatter_gather": "on", 
                "tcp_segmentation_offload": "on", 
                "tx_checksum_fcoe_crc": "off [fixed]", 
                "tx_checksum_ip_generic": "on", 
                "tx_checksum_ipv4": "off", 
                "tx_checksum_ipv6": "off", 
                "tx_checksum_sctp": "off [fixed]", 
                "tx_checksum_unneeded": "off", 
                "tx_checksumming": "on", 
                "tx_fcoe_segmentation": "off [fixed]", 
                "tx_gre_segmentation": "off [fixed]", 
                "tx_gso_robust": "off [fixed]", 
                "tx_lockless": "off [fixed]", 
                "tx_scatter_gather": "on", 
                "tx_scatter_gather_fraglist": "off [fixed]", 
                "tx_tcp6_segmentation": "off", 
                "tx_tcp_ecn_segmentation": "off", 
                "tx_tcp_segmentation": "on", 
                "tx_udp_tnl_segmentation": "off [fixed]", 
                "tx_vlan_offload": "on [fixed]", 
                "udp_fragmentation_offload": "off [fixed]", 
                "vlan_challenged": "off [fixed]"
            }, 
            "hw_timestamp_filters": [], 
            "ipv4": {
                "address": "192.168.0.218", 
                "broadcast": "192.168.0.255", 
                "netmask": "255.255.255.0", 
                "network": "192.168.0.0"
            }, 
            "ipv6": [
                {
                    "address": "fe80::20c:29ff:fee8:f67b", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 
            "macaddress": "00:0c:29:e8:f6:7b", 
            "module": "e1000", 
            "mtu": 1500, 
            "pciid": "0000:02:01.0", 
            "promisc": false, 
            "speed": 1000, 
            "timestamping": [
                "rx_software", 
                "software"
            ], 
            "type": "ether"
        }
    }
    
    PLAY RECAP **********************************************************************************************************
    192.168.0.218              : ok=2    changed=0    unreachable=0    failed=0   
    
    [root@test ~]#
    

      說明:以上playbook就實現了對ansible_eth0這個變量進行調試並打印,可以看到ansible_eth0是一個相對比較複雜的變量,裡面包含了字典,列表混合一起的一個大字典。

    我們可以看到ansible_eht0裡面包含了很多內容,我們要想讀取其中的IPV4地址,我們可以採用“.”或者下標的方式去訪問,如下所示

    [root@test ~]#cat test.yml 
    ---
    - hosts: 192.168.0.218
      remote_user: root
      
      tasks:
        - name: print ipv4  
          shell: echo {{ ansible_eth0["ipv4"]["address"] }} 
        - name: print mac
          shell: echo  {{ ansible_eth0.macaddress }}
    [root@test ~]#ansible-playbook test.yml -v
    Using /etc/ansible/ansible.cfg as config file
    
    PLAY [192.168.0.218] ************************************************************************************************
    
    TASK [Gathering Facts] **********************************************************************************************
    ok: [192.168.0.218]
    
    TASK [print ipv4] ***************************************************************************************************
    changed: [192.168.0.218] => {"changed": true, "cmd": "echo 192.168.0.218", "delta": "0:00:00.001680", "end": "2019-11-17 18:30:21.926368", "rc": 0, "start": "2019-11-17 18:30:21.924688", "stderr": "", "stderr_lines": [], "stdout": "192.168.0.218", "stdout_lines": ["192.168.0.218"]}
    
    TASK [print mac] ****************************************************************************************************
    changed: [192.168.0.218] => {"changed": true, "cmd": "echo 00:0c:29:e8:f6:7b", "delta": "0:00:00.001746", "end": "2019-11-17 18:30:22.650541", "rc": 0, "start": "2019-11-17 18:30:22.648795", "stderr": "", "stderr_lines": [], "stdout": "00:0c:29:e8:f6:7b", "stdout_lines": ["00:0c:29:e8:f6:7b"]}
    
    PLAY RECAP **********************************************************************************************************
    192.168.0.218              : ok=3    changed=2    unreachable=0    failed=0   
    
    [root@test ~]#

      說明:由此可以看出ansible多級變量的調用,使用中括號和點號都是可以的

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開